From d28a7880671f87755696e8b5ab2e767daea4c5f2 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 30 Jun 2021 15:22:11 +0530 Subject: [PATCH 01/68] dip --- .../appsmith/PositionedContainer.tsx | 30 +- .../editorComponents/DragLayerComponent.tsx | 93 ++-- .../editorComponents/DraggableComponent.tsx | 39 +- .../editorComponents/DropTargetComponent.tsx | 23 +- .../components/editorComponents/Dropzone.tsx | 2 +- .../editorComponents/ResizableComponent.tsx | 2 +- .../src/constants/ReduxActionConstants.tsx | 1 + .../src/pages/common/CanvasDraggingArena.tsx | 507 ++++++++++++++++++ .../src/pages/common/CanvasSelectionArena.tsx | 22 +- .../reducers/uiReducers/dragResizeReducer.ts | 16 + app/client/src/utils/helpers.tsx | 64 +++ .../src/utils/hooks/dragResizeHooks.tsx | 14 + app/client/src/widgets/ContainerWidget.tsx | 17 +- 13 files changed, 744 insertions(+), 86 deletions(-) create mode 100644 app/client/src/pages/common/CanvasDraggingArena.tsx diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index 5e5bed61245d..8f6671f81200 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -6,10 +6,16 @@ import styled from "styled-components"; import { useClickOpenPropPane } from "utils/hooks/useClickOpenPropPane"; import { stopEventPropagation } from "utils/AppsmithUtils"; import { Layers } from "constants/Layers"; +import { useSelector } from "react-redux"; +import { AppState } from "reducers"; +import { getSelectedWidgets } from "selectors/ui"; -const PositionedWidget = styled.div` +const PositionedWidget = styled.div<{ isDragging: boolean }>` &:hover { - z-index: ${Layers.positionedWidget + 1} !important; + z-index: ${(props) => + props.isDragging + ? Layers.positionedWidget + : Layers.positionedWidget + 1} !important; } `; type PositionedContainerProps = { @@ -38,6 +44,12 @@ export function PositionedContainer(props: PositionedContainerProps) { .toLowerCase()}` ); }, [props.widgetType, props.widgetId]); + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); + const selectedWidgets = useSelector(getSelectedWidgets); + const isThisWidgetDragging = + isDragging && selectedWidgets.includes(props.widgetId); const containerStyle: CSSProperties = useMemo(() => { return { position: "absolute", @@ -47,12 +59,21 @@ export function PositionedContainer(props: PositionedContainerProps) { width: props.style.componentWidth + (props.style.widthUnit || "px"), padding: padding + "px", zIndex: - props.selected || props.focused + isDragging && + !isThisWidgetDragging && + [ + "CONTAINER_WIDGET", + "FORM_WIDGET", + "LIST_WIDGET", + "TABS_WIDGET", + ].includes(props.widgetType) + ? 3 + : props.selected || props.focused ? Layers.selectedWidget : Layers.positionedWidget, backgroundColor: "inherit", }; - }, [props.style]); + }, [props.style, isDragging]); const openPropPane = useCallback((e) => openPropertyPane(e, props.widgetId), [ props.widgetId, @@ -65,6 +86,7 @@ export function PositionedContainer(props: PositionedContainerProps) { data-testid="test-widget" id={props.widgetId} key={`positioned-container-${props.widgetId}`} + isDragging onClick={stopEventPropagation} // Positioned Widget is the top enclosure for all widgets and clicks on/inside the widget should not be propogated/bubbled out of this Container. onClickCapture={openPropPane} diff --git a/app/client/src/components/editorComponents/DragLayerComponent.tsx b/app/client/src/components/editorComponents/DragLayerComponent.tsx index a26bae3b0216..aab74627450c 100644 --- a/app/client/src/components/editorComponents/DragLayerComponent.tsx +++ b/app/client/src/components/editorComponents/DragLayerComponent.tsx @@ -1,7 +1,6 @@ import React, { useContext, useEffect, RefObject, useRef } from "react"; import styled from "styled-components"; import { useDragLayer, XYCoord } from "react-dnd"; -import DropZone from "./Dropzone"; import { noCollision, currentDropRow } from "utils/WidgetPropsUtils"; import { OccupiedSpace } from "constants/editorConstants"; import { @@ -9,8 +8,6 @@ import { GridDefaults, } from "constants/WidgetConstants"; import { DropTargetContext } from "./DropTargetComponent"; -import { scrollElementIntoParentCanvasView } from "utils/helpers"; -import { getNearestParentCanvas } from "utils/generators"; const GRID_POINT_SIZE = 1; const WrappedDragLayer = styled.div<{ columnWidth: number; @@ -63,40 +60,37 @@ type DragLayerProps = { function DragLayerComponent(props: DragLayerProps) { const { updateDropTargetRows } = useContext(DropTargetContext); const dropTargetMask: RefObject = React.useRef(null); - const dropZoneRef = React.useRef(null); - useEffect(() => { - const el = dropZoneRef.current; - const scrollParent: Element | null = getNearestParentCanvas( - dropTargetMask.current, - ); - if (dropTargetMask.current) { - if (el && props.canDropTargetExtend) { - scrollElementIntoParentCanvasView(el, scrollParent); - } - } - }); + // useEffect(() => { + // const el = dropZoneRef.current; + // const scrollParent: Element | null = getNearestParentCanvas( + // dropTargetMask.current, + // ); + // if (dropTargetMask.current) { + // if (el && props.canDropTargetExtend) { + // scrollElementIntoParentCanvasView(el, scrollParent); + // } + // } + // }); const dropTargetOffset = useRef({ x: 0, y: 0, }); - const { canDrop, currentOffset, isDragging, widget } = useDragLayer( - (monitor) => ({ - isDragging: monitor.isDragging(), - currentOffset: monitor.getSourceClientOffset(), - widget: monitor.getItem(), - canDrop: noCollision( - monitor.getSourceClientOffset() as XYCoord, - props.parentColumnWidth, - props.parentRowHeight, - monitor.getItem(), - dropTargetOffset.current, - props.occupiedSpaces, - props.parentRows, - props.parentCols, - ), - }), - ); + const { currentOffset, isDragging, widget } = useDragLayer((monitor) => ({ + isDragging: monitor.isDragging(), + currentOffset: monitor.getSourceClientOffset(), + widget: monitor.getItem(), + canDrop: noCollision( + monitor.getSourceClientOffset() as XYCoord, + props.parentColumnWidth, + props.parentRowHeight, + monitor.getItem(), + dropTargetOffset.current, + props.occupiedSpaces, + props.parentRows, + props.parentCols, + ), + })); if ( currentOffset && @@ -114,14 +108,14 @@ function DragLayerComponent(props: DragLayerProps) { updateDropTargetRows && updateDropTargetRows(widget.widgetId, row); } - let widgetWidth = 0; - let widgetHeight = 0; - if (widget) { - widgetWidth = widget.columns - ? widget.columns - : widget.rightColumn - widget.leftColumn; - widgetHeight = widget.rows ? widget.rows : widget.bottomRow - widget.topRow; - } + // let widgetWidth = 0; + // let widgetHeight = 0; + // if (widget) { + // widgetWidth = widget.columns + // ? widget.columns + // : widget.rightColumn - widget.leftColumn; + // widgetHeight = widget.rows ? widget.rows : widget.bottomRow - widget.topRow; + // } useEffect(() => { const el = dropTargetMask.current; if (el) { @@ -150,7 +144,6 @@ function DragLayerComponent(props: DragLayerProps) { We can be sure that the parent offset has been calculated when the coordiantes are not [0,0]. */ - const isParentOffsetCalculated = dropTargetOffset.current.x !== 0; return ( - {props.visible && - props.isOver && - currentOffset && - isParentOffsetCalculated && ( - - )} - + /> ); } export default DragLayerComponent; diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 14a4758b3259..113f61cb7360 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -1,9 +1,9 @@ -import React, { CSSProperties } from "react"; +import React, { CSSProperties, useState } from "react"; import styled from "styled-components"; import { WidgetProps } from "widgets/BaseWidget"; import { useDrag, DragSourceMonitor } from "react-dnd"; import { WIDGET_PADDING } from "constants/WidgetConstants"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { AppState } from "reducers"; import { getColorWithOpacity } from "constants/DefaultTheme"; import { @@ -14,6 +14,7 @@ import { import AnalyticsUtil from "utils/AnalyticsUtil"; import { commentModeSelector } from "selectors/commentsSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { selectWidgetInitAction } from "actions/widgetSelectionActions"; const DraggableWrapper = styled.div` display: block; @@ -71,7 +72,7 @@ function DraggableComponent(props: DraggableComponentProps) { // Dispatch hook handy to set any `DraggableComponent` as dragging/ not dragging // The value is boolean - const { setIsDragging } = useWidgetDragResize(); + const { setDragItemsInitialParent, setIsDragging } = useWidgetDragResize(); // This state tells us which widget is selected // The value is the widgetId of the selected widget @@ -105,7 +106,7 @@ function DraggableComponent(props: DraggableComponentProps) { (state: AppState) => state.ui.widgetDragResize.isDraggingDisabled, ); - const [{ isCurrentWidgetDragging }, drag] = useDrag({ + const [{ isCurrentWidgetDragging }] = useDrag({ item: props as WidgetProps, collect: (monitor: DragSourceMonitor) => ({ isCurrentWidgetDragging: monitor.isDragging(), @@ -121,7 +122,6 @@ function DraggableComponent(props: DraggableComponentProps) { showTableFilterPane && showTableFilterPane(); // Tell the rest of the application that a widget has started dragging setIsDragging && setIsDragging(true); - AnalyticsUtil.logEvent("WIDGET_DRAG", { widgetName: props.widgetName, widgetType: props.type, @@ -175,7 +175,9 @@ function DraggableComponent(props: DraggableComponentProps) { { + if (mightBeDragging) { + e.preventDefault(); + setDragItemsInitialParent(true, props.parentId || "", props.widgetId); + if (!selectedWidgets.includes(props.widgetId)) { + dispatch(selectWidgetInitAction(props.widgetId)); + } + setMightBeDragging(false); + e.stopPropagation(); + } + }; return ( { + setMightBeDragging(true); + e.stopPropagation(); + }} + onMouseMove={mouseMove} onMouseOver={handleMouseOver} - ref={drag} + onMouseUp={(e) => { + setMightBeDragging(false); + e.stopPropagation(); + }} style={style} > {shouldRenderComponent && props.children} diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index e62988f78879..32ec0ed878f6 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -6,6 +6,7 @@ import React, { createContext, useEffect, memo, + useRef, } from "react"; import styled from "styled-components"; import { useDrop, XYCoord, DropTargetMonitor } from "react-dnd"; @@ -62,8 +63,13 @@ function Onboarding() { an update of the main container's rows */ export const DropTargetContext: Context<{ - updateDropTargetRows?: (widgetId: string, row: number) => boolean; + updateDropTargetRows?: ( + widgetId: string, + widgetBottomRow: number, + ) => number | false; persistDropTargetRows?: (widgetId: string, row: number) => void; + handleBoundsUpdate?: (rect: DOMRect) => void; + dropRef?: React.RefObject; }> = createContext({}); export function DropTargetComponent(props: DropTargetComponentProps) { @@ -135,7 +141,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { ); if (rows < newRows) { setRows(newRows); - return true; + return newRows; } return false; } @@ -150,7 +156,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const isChildResizing = !!isResizing && isChildFocused; // Make this component a drop target - const [{ isExactlyOver, isOver }, drop] = useDrop({ + const [{ isExactlyOver }] = useDrop({ accept: Object.values(WidgetTypes), options: { arePropsEqual: () => { @@ -256,11 +262,16 @@ export function DropTargetComponent(props: DropTargetComponentProps) { props.widgetId === MAIN_CONTAINER_WIDGET_ID ? "0px 0px 0px 1px #DDDDDD" : "0px 0px 0px 1px transparent"; - const dropRef = !props.dropDisabled ? drop : undefined; + const dropRef = useRef(null); return ( } + props.id === "canvas-dragging-0" + ? props.theme.canvasBottomPadding + : 0}px + ); + width: 100%; + image-rendering: -moz-crisp-edges; + image-rendering: -webkit-crisp-edges; + image-rendering: pixelated; + image-rendering: crisp-edges; + overflow-y: auto; +`; + +export interface SelectedArenaDimensions { + top: number; + left: number; + width: number; + height: number; +} + +const noCollision = ( + clientOffset: XYCoord, + colWidth: number, + rowHeight: number, + dropTargetOffset: XYCoord, + widgetWidth: number, + widgetHeight: number, + widgetId: string, + occupiedSpaces?: OccupiedSpace[], + rows?: number, + cols?: number, +): boolean => { + if (clientOffset && dropTargetOffset) { + // if (widget.detachFromLayout) { + // return true; + // } + const [left, top] = getDropZoneOffsets( + colWidth, + rowHeight, + clientOffset as XYCoord, + dropTargetOffset, + ); + if (left < 0 || top < 0) { + return false; + } + const currentOffset = { + left, + right: left + widgetWidth, + top, + bottom: top + widgetHeight, + }; + return ( + !isDropZoneOccupied(currentOffset, widgetId, occupiedSpaces) && + !isWidgetOverflowingParentBounds({ rows, cols }, currentOffset) + ); + } + return false; +}; + +export function CanvasDraggingArena({ + // childWidgets, + noPad, + snapColumnSpace, + snapRows, + snapRowSpace, + widgetId, +}: { + // childWidgets: string[]; + noPad?: boolean; + snapColumnSpace: number; + snapRows: number; + snapRowSpace: number; + widgetId: string; +}) { + const dragParent = useSelector( + (state: AppState) => state.ui.widgetDragResize.dragParent, + ); + const selectedWidgets = useSelector(getSelectedWidgets); + const occupiedSpaces = useSelector(getOccupiedSpaces) || {}; + const childrenOccupiedSpaces: OccupiedSpace[] = + occupiedSpaces[dragParent] || []; + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); + const allWidgets = useSelector(getWidgets); + // const widget = useSelector((state: AppState) => getWidget(state, widgetId)); + const dragCenter = useSelector( + (state: AppState) => state.ui.widgetDragResize.dragCenter, + ); + const dragCenterSpace = childrenOccupiedSpaces.find( + (each) => each.id === dragCenter, + ); + const rectanglesToDraw = childrenOccupiedSpaces + .filter((each) => selectedWidgets.includes(each.id)) + .map((each) => ({ + top: each.top * snapRowSpace + (noPad ? 0 : CONTAINER_GRID_PADDING), + left: each.left * snapColumnSpace + (noPad ? 0 : CONTAINER_GRID_PADDING), + width: (each.right - each.left) * snapColumnSpace, + height: (each.bottom - each.top) * snapRowSpace, + columnWidth: each.right - each.left, + rowHeight: each.bottom - each.top, + widgetId: each.id, + isNotColliding: true, + })); + const { setIsDragging } = useWidgetDragResize(); + const canvasRef = React.useRef(null); + const { persistDropTargetRows, updateDropTargetRows } = useContext( + DropTargetContext, + ); + + const scrollToKeepUp = ( + drawingBlocks: { + left: number; + top: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + }[], + ) => { + if (isDragging) { + const dragCenterBlock = drawingBlocks.find( + (each) => each.widgetId === dragCenter, + ); + // const el = canvasRef?.current; + const scrollParent: Element | null = getNearestParentCanvas( + canvasRef.current, + ); + if (canvasRef.current) { + // if (el && props.canDropTargetExtend) { + if (dragCenterBlock) { + scrollElementIntoParentCanvasView2( + dragCenterBlock, + scrollParent, + canvasRef.current, + ); + } + } + } + }; + + const updateRows = ( + drawingBlocks: { + left: number; + top: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + }[], + ) => { + const sortedByTopBlocks = drawingBlocks.sort( + (each1, each2) => each2.top + each2.height - (each1.top + each1.height), + ); + const bottomMostBlock = sortedByTopBlocks[0]; + const [, top] = getDropZoneOffsets( + snapColumnSpace, + snapRowSpace, + { + x: bottomMostBlock.left, + y: bottomMostBlock.top + bottomMostBlock.height, + } as XYCoord, + { x: 0, y: 0 }, + ); + // const snappedXY = getSnappedXY( + // snapColumnSpace, + // snapRowSpace, + // { + // x: bottomMostBlock.left, + // y: bottomMostBlock.top + bottomMostBlock.height, + // }, + // { + // x: currentOffset?.x || 0, + // y: currentOffset?.y || 0, + // }, + // ); + // const canvasTopBound = canvasRef?.current?.getBoundingClientRect().top || 0; + + // const row = currentDropRow( + // snapRowSpace, + // snappedXY.Y, + // currentOffset?.y || 0, + // widget, + // ); + // const bottomMostRow = bottomMostBlock.top + bottomMostBlock.height; + console.log({ drawingBlocks, bottomMostBlock, top }); + return updateDropTargetRows && updateDropTargetRows(widgetId, top + 20); + }; + const { updateWidget } = useContext(EditorContext); + + const onDrop = ( + drawingBlocks: { + left: number; + top: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + isNotColliding: boolean; + }[], + ) => { + const cannotDrop = drawingBlocks.some((each) => { + return !each.isNotColliding; + }); + if (!cannotDrop) { + drawingBlocks.forEach((each) => { + const widget = allWidgets[each.widgetId]; + const updateWidgetParams = widgetOperationParams( + widget, + { x: each.left, y: each.top }, + { x: 0, y: 0 }, + snapColumnSpace, + snapRowSpace, + widget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : widgetId, + ); + + // const widgetBottomRow = getWidgetBottomRow(widget, updateWidgetParams); + const widgetBottomRow = + updateWidgetParams.payload.topRow + + (updateWidgetParams.payload.rows || widget.bottomRow - widget.topRow); + persistDropTargetRows && + persistDropTargetRows(widget.widgetId, widgetBottomRow); + + /* Finally update the widget */ + updateWidget && + updateWidget( + updateWidgetParams.operation, + updateWidgetParams.widgetId, + updateWidgetParams.payload, + ); + }); + } + }; + + useEffect(() => { + if (isDragging && canvasRef.current) { + const rows = snapRows; + let canvasIsDragging = false; + const draggingCanvas: any = canvasRef.current; + const scale = 1; + + // draggingCanvas.style.padding = `${noPad ? 0 : CONTAINER_GRID_PADDING}px`; + const { height, width } = draggingCanvas.getBoundingClientRect(); + draggingCanvas.width = width * scale; + draggingCanvas.height = height * scale; + const differentParent = dragParent !== widgetId; + const parentDiff = { + top: + differentParent && dragCenterSpace + ? dragCenterSpace.top * snapRowSpace + + (noPad ? 0 : CONTAINER_GRID_PADDING) + : noPad + ? 0 + : CONTAINER_GRID_PADDING, + left: + differentParent && dragCenterSpace + ? dragCenterSpace.left * snapColumnSpace + + (noPad ? 0 : CONTAINER_GRID_PADDING) + : noPad + ? 0 + : CONTAINER_GRID_PADDING, + }; + let newRectanglesToDraw: { + top: number; + left: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + isNotColliding: boolean; + }[] = []; + const canvasCtx = draggingCanvas.getContext("2d"); + canvasCtx.globalCompositeOperation = "destination-over"; + // const drawDragLayer = () => { + // const canvas = canvasRef.current || draggingCanvas; + // const { width } = canvas.getBoundingClientRect(); + + // canvasCtx.beginPath(); // clear path if it has been used previously + // // modify method to add to path instead + // const draw = (x: any, y: any, width: any, height: any) => { + // canvasCtx.fillStyle = `${"rgb(0, 0, 0, 1)"}`; + // canvasCtx.strokeStyle = `${"rgb(0, 0, 0, 1)"}`; + // canvasCtx.rect(x, y, width, height); + // }; + // for ( + // let x = noPad ? 0 : CONTAINER_GRID_PADDING; + // x < width; + // x += snapColumnSpace + // ) { + // for ( + // let y = noPad ? 0 : CONTAINER_GRID_PADDING; + // y < (rows + 1) * 10 - 200; + // y += snapRowSpace + // ) { + // draw(x, y, 1, 1); + // } + // } + + // // when done, fill once + // canvasCtx.fill(); + // }; + // canvasCtx.scale(scale, scale); + + const startPoints = { + left: 0, + top: 0, + }; + const onMouseUp = () => { + startPoints.left = 0; + startPoints.top = 0; + setIsDragging(false); + onMouseOut(); + onDrop(newRectanglesToDraw); + }; + const onMouseOut = () => { + draggingCanvas.style.zIndex = null; + canvasCtx.clearRect(0, 0, width, height); + canvasIsDragging = false; + }; + const onMouseDown = (e: any) => { + if (isDragging && !canvasIsDragging) { + canvasIsDragging = true; + if ( + dragParent === widgetId && + startPoints.left === 0 && + startPoints.top === 0 + ) { + startPoints.left = e.offsetX; + startPoints.top = e.offsetY; + } + draggingCanvas.style.zIndex = 2; + } + }; + const onMouseMove = (e: any) => { + if (canvasIsDragging) { + // console.log(startPoints, e.offsetX, draggingCanvas.offsetLeft); + canvasCtx.clearRect(0, 0, width, height); + const diff = { + left: e.offsetX - startPoints.left - parentDiff.left, + top: e.offsetY - startPoints.top - parentDiff.top, + }; + const currentOccSpaces = occupiedSpaces[widgetId]; + const occSpaces: OccupiedSpace[] = + dragParent === widgetId + ? childrenOccupiedSpaces.filter( + (each) => !selectedWidgets.includes(each.id), + ) + : currentOccSpaces; + newRectanglesToDraw = rectanglesToDraw.map((each) => ({ + ...each, + left: each.left + diff.left, + top: each.top + diff.top, + isNotColliding: noCollision( + { x: each.left + diff.left, y: each.top + diff.top }, + snapColumnSpace, + snapRowSpace, + { x: 0, y: 0 }, + each.columnWidth, + each.rowHeight, + each.widgetId, + occSpaces, + rows, + GridDefaults.DEFAULT_GRID_COLUMNS, + ), + })); + updateRows(newRectanglesToDraw); + // const rowDiff = newRows && rows !== newRows ? newRows - rows : 0; + // rows = newRows && newRows !== rows ? newRows : rows; + // // if (rowDiff) { + // drawDragLayer(); + // // } + // if (rowDiff) { + // startPoints.top = startPoints.top + rowDiff * snapRowSpace; + // newRectanglesToDraw = newRectanglesToDraw.map((each) => { + // each.top = each.top + rowDiff * snapRowSpace; + // return each; + // }); + // } + + scrollToKeepUp(newRectanglesToDraw); + newRectanglesToDraw.forEach((each) => { + drawRectangle(each); + }); + } else { + onMouseDown(e); + } + }; + const drawRectangle = (selectionDimensions: { + top: number; + left: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + isNotColliding: boolean; + }) => { + const canvasCtx = + canvasRef.current?.getContext("2d") || + draggingCanvas.getContext("2d"); + const snappedXY = getSnappedXY( + snapColumnSpace, + snapRowSpace, + { + x: selectionDimensions.left, + y: selectionDimensions.top, + }, + { + x: 0, + y: 0, + }, + ); + + canvasCtx.fillStyle = `${ + selectionDimensions.isNotColliding ? "rgb(104, 113, 239, 0.6)" : "red" + }`; + canvasCtx.fillRect( + selectionDimensions.left + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.top + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.width, + selectionDimensions.height, + ); + canvasCtx.fillStyle = `${ + selectionDimensions.isNotColliding ? "rgb(233, 250, 243, 0.6)" : "red" + }`; + const strokeWidth = 1; + canvasCtx.setLineDash([3]); + canvasCtx.strokeStyle = "rgb(104, 113, 239)"; + canvasCtx.strokeRect( + snappedXY.X + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), + snappedXY.Y + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.width - strokeWidth, + selectionDimensions.height - strokeWidth, + ); + }; + const startDragging = () => { + draggingCanvas.addEventListener("mousemove", onMouseMove, false); + draggingCanvas.addEventListener("mouseup", onMouseUp, false); + draggingCanvas.addEventListener("mouseover", onMouseDown, false); + draggingCanvas.addEventListener("mouseout", onMouseOut, false); + + if (canvasIsDragging) { + // fix_dpi(); + // drawDragLayer(); + rectanglesToDraw.forEach((each) => { + drawRectangle(each); + }); + } + }; + startDragging(); + if (dragParent === widgetId) { + draggingCanvas.style.zIndex = 2; + } + return () => { + draggingCanvas.removeEventListener("mousemove", onMouseMove); + draggingCanvas.removeEventListener("mouseup", onMouseUp); + draggingCanvas.removeEventListener("mouseenter", onMouseDown); + draggingCanvas.removeEventListener("mouseleave", onMouseOut); + }; + } + }, [isDragging]); + return isDragging ? ( + + ) : null; +} +CanvasDraggingArena.displayName = "CanvasDraggingArena"; diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index cc79994f755b..84e19a2c60a2 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -21,7 +21,11 @@ const StyledSelectionCanvas = styled.canvas` position: absolute; top: 0px; left: 0px; - height: calc(100% + ${(props) => props.theme.canvasBottomPadding}px); + height: calc( + 100% + + ${(props) => + props.id === "canvas-0" ? props.theme.canvasBottomPadding : 0}px + ); width: 100%; overflow-y: auto; `; @@ -37,7 +41,9 @@ export const CanvasSelectionArena = memo( ({ widgetId }: { widgetId: string }) => { const dispatch = useDispatch(); const appMode = useSelector(getAppMode); - + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); const mainContainer = useSelector((state: AppState) => getWidget(state, MAIN_CONTAINER_WIDGET_ID), ); @@ -69,10 +75,12 @@ export const CanvasSelectionArena = memo( [widgetId], ); useEffect(() => { - if (appMode === APP_MODE.EDIT) { + if (appMode === APP_MODE.EDIT && !isDragging) { const selectionCanvas: any = document.getElementById( `canvas-${widgetId}`, ); + const scale = window.devicePixelRatio; + const canvasCtx = selectionCanvas.getContext("2d"); const initRectangle = (): SelectedArenaDimensions => ({ top: 0, @@ -87,10 +95,11 @@ export const CanvasSelectionArena = memo( const init = () => { const { height, width } = selectionCanvas.getBoundingClientRect(); if (height && width) { - selectionCanvas.width = mainContainer.rightColumn; + selectionCanvas.width = mainContainer.rightColumn * scale; selectionCanvas.height = - mainContainer.bottomRow + theme.canvasBottomPadding; + (mainContainer.bottomRow + theme.canvasBottomPadding) * scale; } + canvasCtx.scale(scale, scale); selectionCanvas.addEventListener("click", onClick, false); selectionCanvas.addEventListener("mousedown", onMouseDown, false); selectionCanvas.addEventListener("mouseup", onMouseUp, false); @@ -239,9 +248,10 @@ export const CanvasSelectionArena = memo( mainContainer.rightColumn, mainContainer.bottomRow, mainContainer.minHeight, + isDragging, ]); - return appMode === APP_MODE.EDIT ? ( + return appMode === APP_MODE.EDIT && !isDragging ? ( { state.isDraggingDisabled = action.payload.isDraggingDisabled; }, + [ReduxActionTypes.SET_NEW_WIDGET_DRAGGING]: ( + state: WidgetDragResizeState, + action: ReduxAction<{ + isDragging: boolean; + dragParent: string; + dragCenter: string; + }>, + ) => { + state.dragParent = action.payload.dragParent; + state.isDragging = action.payload.isDragging; + state.dragCenter = action.payload.dragCenter; + }, [ReduxActionTypes.SET_WIDGET_DRAGGING]: ( state: WidgetDragResizeState, action: ReduxAction<{ isDragging: boolean }>, @@ -109,6 +123,8 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { export type WidgetDragResizeState = { isDraggingDisabled: boolean; isDragging: boolean; + dragParent: string; + dragCenter: string; isResizing: boolean; lastSelectedWidget?: string; focusedWidget?: string; diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index 39974e88b430..3d9c2f6810df 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -72,6 +72,43 @@ export const getScrollByPixels = function( return 0; }; +export const getScrollByPixels2 = function( + elem: { + left: number; + top: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + }, + scrollParent: Element, + child: Element, +): number { + // const bounding = elem.getBoundingClientRect(); + const scrollParentBounds = scrollParent.getBoundingClientRect(); + const scrollChildBounds = child.getBoundingClientRect(); + const scrollAmount = + GridDefaults.CANVAS_EXTENSION_OFFSET * GridDefaults.DEFAULT_GRID_ROW_HEIGHT; + + if ( + elem.top + scrollChildBounds.top > 0 && + elem.top + + scrollChildBounds.top - + SCROLL_THESHOLD - + scrollParentBounds.top < + SCROLL_THESHOLD + ) + return -scrollAmount; + if ( + scrollParentBounds.bottom - + (elem.top + elem.height + scrollChildBounds.top + SCROLL_THESHOLD) < + SCROLL_THESHOLD + ) + return scrollAmount; + return 0; +}; + export const scrollElementIntoParentCanvasView = ( el: Element | null, parent: Element | null, @@ -90,6 +127,33 @@ export const scrollElementIntoParentCanvasView = ( } }; +export const scrollElementIntoParentCanvasView2 = ( + el: { + left: number; + top: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + } | null, + parent: Element | null, + child: Element | null, +) => { + if (el) { + const scrollParent = parent; + if (scrollParent && child) { + const scrollBy: number = getScrollByPixels2(el, scrollParent, child); + if (scrollBy < 0 && scrollParent.scrollTop > 0) { + scrollParent.scrollBy({ top: scrollBy, behavior: "smooth" }); + } + if (scrollBy > 0) { + scrollParent.scrollBy({ top: scrollBy, behavior: "smooth" }); + } + } + } +}; + export const removeSpecialChars = (value: string, limit?: number) => { const separatorRegex = /\W+/; return value diff --git a/app/client/src/utils/hooks/dragResizeHooks.tsx b/app/client/src/utils/hooks/dragResizeHooks.tsx index e6bf915befc6..14aecd9902a6 100644 --- a/app/client/src/utils/hooks/dragResizeHooks.tsx +++ b/app/client/src/utils/hooks/dragResizeHooks.tsx @@ -109,6 +109,20 @@ export const useWidgetDragResize = () => { }, [dispatch], ), + setDragItemsInitialParent: useCallback( + (isDragging: boolean, dragParent: string, dragCenter: string) => { + if (isDragging) { + document.body.classList.add("dragging"); + } else { + document.body.classList.remove("dragging"); + } + dispatch({ + type: ReduxActionTypes.SET_NEW_WIDGET_DRAGGING, + payload: { isDragging, dragParent, dragCenter }, + }); + }, + [dispatch], + ), setIsResizing: useCallback( (isResizing: boolean) => { dispatch({ diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index c544f313b41a..35b8a3aff342 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -17,6 +17,8 @@ import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget"; import { VALIDATION_TYPES } from "constants/WidgetValidation"; import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; import { CanvasSelectionArena } from "pages/common/CanvasSelectionArena"; +import { CanvasDraggingArena } from "pages/common/CanvasDraggingArena"; +import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; class ContainerWidget extends BaseWidget< ContainerWidgetProps, WidgetState @@ -122,15 +124,26 @@ class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { + // const childWidgets = (props.children || []).map((each) => { + // return each.widgetId; + // }); + const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); return ( - {this.props.widgetId === MAIN_CONTAINER_WIDGET_ID && ( - + {props.type === "CANVAS_WIDGET" && ( + )} + {/* without the wrapping div onClick events are triggered twice */} <>{this.renderChildren()} From 0498057ba05a8871c7b41c37501fc83e879c9456 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 2 Jul 2021 00:46:13 +0530 Subject: [PATCH 02/68] dip --- .../appsmith/PositionedContainer.tsx | 6 +- .../editorComponents/DragLayerComponent.tsx | 63 ++- .../editorComponents/DraggableComponent.tsx | 14 +- .../editorComponents/DropTargetComponent.tsx | 13 +- .../src/constants/ReduxActionConstants.tsx | 1 + app/client/src/pages/Editor/WidgetCard.tsx | 7 +- .../src/pages/common/CanvasDraggingArena.tsx | 382 ++++++++++++------ .../src/pages/common/CanvasSelectionArena.tsx | 2 +- .../reducers/uiReducers/dragResizeReducer.ts | 12 + app/client/src/utils/helpers.tsx | 2 +- .../src/utils/hooks/dragResizeHooks.tsx | 14 + 11 files changed, 346 insertions(+), 170 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index 8f6671f81200..fa03df4b993c 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -47,6 +47,9 @@ export function PositionedContainer(props: PositionedContainerProps) { const isDragging = useSelector( (state: AppState) => state.ui.widgetDragResize.isDragging, ); + const isResizing = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); const selectedWidgets = useSelector(getSelectedWidgets); const isThisWidgetDragging = isDragging && selectedWidgets.includes(props.widgetId); @@ -60,6 +63,7 @@ export function PositionedContainer(props: PositionedContainerProps) { padding: padding + "px", zIndex: isDragging && + !isResizing && !isThisWidgetDragging && [ "CONTAINER_WIDGET", @@ -73,7 +77,7 @@ export function PositionedContainer(props: PositionedContainerProps) { : Layers.positionedWidget, backgroundColor: "inherit", }; - }, [props.style, isDragging]); + }, [props.style, isDragging, isResizing]); const openPropPane = useCallback((e) => openPropertyPane(e, props.widgetId), [ props.widgetId, diff --git a/app/client/src/components/editorComponents/DragLayerComponent.tsx b/app/client/src/components/editorComponents/DragLayerComponent.tsx index aab74627450c..4bae37f88707 100644 --- a/app/client/src/components/editorComponents/DragLayerComponent.tsx +++ b/app/client/src/components/editorComponents/DragLayerComponent.tsx @@ -1,13 +1,12 @@ -import React, { useContext, useEffect, RefObject, useRef } from "react"; +import React, { RefObject, useRef } from "react"; import styled from "styled-components"; import { useDragLayer, XYCoord } from "react-dnd"; -import { noCollision, currentDropRow } from "utils/WidgetPropsUtils"; +import { noCollision } from "utils/WidgetPropsUtils"; import { OccupiedSpace } from "constants/editorConstants"; import { CONTAINER_GRID_PADDING, GridDefaults, } from "constants/WidgetConstants"; -import { DropTargetContext } from "./DropTargetComponent"; const GRID_POINT_SIZE = 1; const WrappedDragLayer = styled.div<{ columnWidth: number; @@ -58,7 +57,7 @@ type DragLayerProps = { }; function DragLayerComponent(props: DragLayerProps) { - const { updateDropTargetRows } = useContext(DropTargetContext); + // const { updateDropTargetRows } = useContext(DropTargetContext); const dropTargetMask: RefObject = React.useRef(null); // useEffect(() => { // const el = dropZoneRef.current; @@ -76,7 +75,7 @@ function DragLayerComponent(props: DragLayerProps) { x: 0, y: 0, }); - const { currentOffset, isDragging, widget } = useDragLayer((monitor) => ({ + const { isDragging } = useDragLayer((monitor) => ({ isDragging: monitor.isDragging(), currentOffset: monitor.getSourceClientOffset(), widget: monitor.getItem(), @@ -92,21 +91,21 @@ function DragLayerComponent(props: DragLayerProps) { ), })); - if ( - currentOffset && - props.isOver && - props.canDropTargetExtend && - isDragging - ) { - const row = currentDropRow( - props.parentRowHeight, - dropTargetOffset.current.y, - currentOffset.y, - widget, - ); + // if ( + // currentOffset && + // props.isOver && + // props.canDropTargetExtend && + // isDragging + // ) { + // const row = currentDropRow( + // props.parentRowHeight, + // dropTargetOffset.current.y, + // currentOffset.y, + // widget, + // ); - updateDropTargetRows && updateDropTargetRows(widget.widgetId, row); - } + // updateDropTargetRows && updateDropTargetRows(widget.widgetId, row); + // } // let widgetWidth = 0; // let widgetHeight = 0; @@ -116,19 +115,19 @@ function DragLayerComponent(props: DragLayerProps) { // : widget.rightColumn - widget.leftColumn; // widgetHeight = widget.rows ? widget.rows : widget.bottomRow - widget.topRow; // } - useEffect(() => { - const el = dropTargetMask.current; - if (el) { - const rect = el.getBoundingClientRect(); - if ( - rect.x !== dropTargetOffset.current.x || - rect.y !== dropTargetOffset.current.y - ) { - dropTargetOffset.current = { x: rect.x, y: rect.y }; - props.onBoundsUpdate && props.onBoundsUpdate(rect); - } - } - }); + // useEffect(() => { + // const el = dropTargetMask.current; + // if (el) { + // const rect = el.getBoundingClientRect(); + // if ( + // rect.x !== dropTargetOffset.current.x || + // rect.y !== dropTargetOffset.current.y + // ) { + // dropTargetOffset.current = { x: rect.x, y: rect.y }; + // props.onBoundsUpdate && props.onBoundsUpdate(rect); + // } + // } + // }); if ( (!isDragging || !props.visible || !props.isOver) && diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 113f61cb7360..82e5a205336a 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -196,7 +196,7 @@ function DraggableComponent(props: DraggableComponentProps) { const dispatch = useDispatch(); const [mightBeDragging, setMightBeDragging] = useState(false); const mouseMove = (e: any) => { - if (mightBeDragging) { + if (mightBeDragging && !isResizingOrDragging) { e.preventDefault(); setDragItemsInitialParent(true, props.parentId || "", props.widgetId); if (!selectedWidgets.includes(props.widgetId)) { @@ -210,14 +210,18 @@ function DraggableComponent(props: DraggableComponentProps) { { - setMightBeDragging(true); - e.stopPropagation(); + if (!isResizingOrDragging) { + setMightBeDragging(true); + e.stopPropagation(); + } }} onMouseMove={mouseMove} onMouseOver={handleMouseOver} onMouseUp={(e) => { - setMightBeDragging(false); - e.stopPropagation(); + if (!isResizingOrDragging) { + setMightBeDragging(false); + e.stopPropagation(); + } }} style={style} > diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 32ec0ed878f6..9a8ab1c9a27e 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -4,7 +4,6 @@ import React, { ReactNode, Context, createContext, - useEffect, memo, useRef, } from "react"; @@ -108,10 +107,10 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const { deselectAll, focusWidget, selectWidget } = useWidgetSelection(); const updateCanvasSnapRows = useCanvasSnapRowsUpdateHook(); - useEffect(() => { - const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); - setRows(snapRows); - }, [props.bottomRow, props.canExtend]); + // useEffect(() => { + // const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); + // setRows(snapRows); + // }, [props.bottomRow, props.canExtend]); const persistDropTargetRows = (widgetId: string, widgetBottomRow: number) => { const newRows = calculateDropTargetRows( @@ -139,8 +138,10 @@ export function DropTargetComponent(props: DropTargetComponentProps) { props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1, occupiedSpacesByChildren, ); + console.log(rows, newRows); + if (rows < newRows) { - setRows(newRows); + setRows(newRows + 1); return newRows; } return false; diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index e5660a2a1515..cc144053ceb5 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -326,6 +326,7 @@ export const ReduxActionTypes: { [key: string]: string } = { FOCUS_WIDGET: "FOCUS_WIDGET", SET_WIDGET_DRAGGING: "SET_WIDGET_DRAGGING", SET_NEW_WIDGET_DRAGGING: "SET_NEW_WIDGET_DRAGGING", + SET_NEW_WIDGET_DRAGGING_2: "SET_NEW_WIDGET_DRAGGING_2", SET_WIDGET_RESIZING: "SET_WIDGET_RESIZING", ADD_TABLE_WIDGET_FROM_QUERY: "ADD_TABLE_WIDGET_FROM_QUERY", SEARCH_APPLICATIONS: "SEARCH_APPLICATIONS", diff --git a/app/client/src/pages/Editor/WidgetCard.tsx b/app/client/src/pages/Editor/WidgetCard.tsx index b3baddc045f2..e70bf9a86d38 100644 --- a/app/client/src/pages/Editor/WidgetCard.tsx +++ b/app/client/src/pages/Editor/WidgetCard.tsx @@ -72,7 +72,7 @@ export const IconLabel = styled.h5` `; function WidgetCard(props: CardProps) { - const { setIsDragging } = useWidgetDragResize(); + const { setDraggingNewWidget } = useWidgetDragResize(); const { deselectAll } = useWidgetSelection(); // Generate a new widgetId which can be used in the future for this widget. const [widgetId, setWidgetId] = useState(generateReactKey()); @@ -83,7 +83,8 @@ function WidgetCard(props: CardProps) { widgetType: props.details.type, widgetName: props.details.widgetCardName, }); - setIsDragging && setIsDragging(true); + setDraggingNewWidget && + setDraggingNewWidget(true, { ...props.details, widgetId }); deselectAll(); }, end: (widget, monitor) => { @@ -94,7 +95,7 @@ function WidgetCard(props: CardProps) { }); // We've finished dragging, generate a new widgetId to be used for next drag. setWidgetId(generateReactKey()); - setIsDragging && setIsDragging(false); + // setDraggingNewWidget && setDraggingNewWidget(false, undefined); }, }); diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 6c9f0dbdd60c..3459da821131 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -24,6 +24,7 @@ import { scrollElementIntoParentCanvasView2 } from "utils/helpers"; import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; import { getWidgets } from "sagas/selectors"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; +import { debounce, throttle } from "lodash"; const StyledSelectionCanvas = styled.canvas` position: absolute; @@ -108,6 +109,9 @@ export function CanvasDraggingArena({ const dragParent = useSelector( (state: AppState) => state.ui.widgetDragResize.dragParent, ); + const isResizing = useSelector( + (state: AppState) => state.ui.widgetDragResize.isResizing, + ); const selectedWidgets = useSelector(getSelectedWidgets); const occupiedSpaces = useSelector(getOccupiedSpaces) || {}; const childrenOccupiedSpaces: OccupiedSpace[] = @@ -115,6 +119,9 @@ export function CanvasDraggingArena({ const isDragging = useSelector( (state: AppState) => state.ui.widgetDragResize.isDragging, ); + const newWidget = useSelector( + (state: AppState) => state.ui.widgetDragResize.newWidget, + ); const allWidgets = useSelector(getWidgets); // const widget = useSelector((state: AppState) => getWidget(state, widgetId)); const dragCenter = useSelector( @@ -123,18 +130,32 @@ export function CanvasDraggingArena({ const dragCenterSpace = childrenOccupiedSpaces.find( (each) => each.id === dragCenter, ); - const rectanglesToDraw = childrenOccupiedSpaces - .filter((each) => selectedWidgets.includes(each.id)) - .map((each) => ({ - top: each.top * snapRowSpace + (noPad ? 0 : CONTAINER_GRID_PADDING), - left: each.left * snapColumnSpace + (noPad ? 0 : CONTAINER_GRID_PADDING), - width: (each.right - each.left) * snapColumnSpace, - height: (each.bottom - each.top) * snapRowSpace, - columnWidth: each.right - each.left, - rowHeight: each.bottom - each.top, - widgetId: each.id, - isNotColliding: true, - })); + const rectanglesToDraw = !newWidget + ? childrenOccupiedSpaces + .filter((each) => selectedWidgets.includes(each.id)) + .map((each) => ({ + top: each.top * snapRowSpace + (noPad ? 0 : CONTAINER_GRID_PADDING), + left: + each.left * snapColumnSpace + (noPad ? 0 : CONTAINER_GRID_PADDING), + width: (each.right - each.left) * snapColumnSpace, + height: (each.bottom - each.top) * snapRowSpace, + columnWidth: each.right - each.left, + rowHeight: each.bottom - each.top, + widgetId: each.id, + isNotColliding: true, + })) + : [ + { + top: 0, + left: 0, + width: newWidget.columns * snapColumnSpace, + height: newWidget.rows * snapRowSpace, + columnWidth: newWidget.columns, + rowHeight: newWidget.rows, + widgetId: newWidget.widgetId, + isNotColliding: true, + }, + ]; const { setIsDragging } = useWidgetDragResize(); const canvasRef = React.useRef(null); const { persistDropTargetRows, updateDropTargetRows } = useContext( @@ -153,18 +174,19 @@ export function CanvasDraggingArena({ }[], ) => { if (isDragging) { - const dragCenterBlock = drawingBlocks.find( - (each) => each.widgetId === dragCenter, + const sortedByTopBlocks = drawingBlocks.sort( + (each1, each2) => each2.top + each2.height - (each1.top + each1.height), ); + const bottomMostBlock = sortedByTopBlocks[0]; // const el = canvasRef?.current; const scrollParent: Element | null = getNearestParentCanvas( canvasRef.current, ); if (canvasRef.current) { // if (el && props.canDropTargetExtend) { - if (dragCenterBlock) { + if (bottomMostBlock) { scrollElementIntoParentCanvasView2( - dragCenterBlock, + bottomMostBlock, scrollParent, canvasRef.current, ); @@ -183,6 +205,7 @@ export function CanvasDraggingArena({ rowHeight: number; widgetId: string; }[], + rows: number, ) => { const sortedByTopBlocks = drawingBlocks.sort( (each1, each2) => each2.top + each2.height - (each1.top + each1.height), @@ -219,7 +242,20 @@ export function CanvasDraggingArena({ // ); // const bottomMostRow = bottomMostBlock.top + bottomMostBlock.height; console.log({ drawingBlocks, bottomMostBlock, top }); - return updateDropTargetRows && updateDropTargetRows(widgetId, top + 20); + // const newRows = calculateDropTargetRows( + // widgetId, + // top, + // minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1, + // occupiedSpaces[widgetId], + // ); + + // if (snapRows < newRows) { + // // setRows(newRows); + // return newRows; + // } + if (top > rows) { + return updateDropTargetRows && updateDropTargetRows(widgetId, top); + } }; const { updateWidget } = useContext(EditorContext); @@ -240,7 +276,7 @@ export function CanvasDraggingArena({ }); if (!cannotDrop) { drawingBlocks.forEach((each) => { - const widget = allWidgets[each.widgetId]; + const widget = newWidget ? newWidget : allWidgets[each.widgetId]; const updateWidgetParams = widgetOperationParams( widget, { x: each.left, y: each.top }, @@ -269,16 +305,15 @@ export function CanvasDraggingArena({ }; useEffect(() => { - if (isDragging && canvasRef.current) { - const rows = snapRows; + if (isDragging && canvasRef.current && !isResizing) { + let rows = snapRows; let canvasIsDragging = false; - const draggingCanvas: any = canvasRef.current; const scale = 1; // draggingCanvas.style.padding = `${noPad ? 0 : CONTAINER_GRID_PADDING}px`; - const { height, width } = draggingCanvas.getBoundingClientRect(); - draggingCanvas.width = width * scale; - draggingCanvas.height = height * scale; + const { height, width } = canvasRef.current.getBoundingClientRect(); + canvasRef.current.width = width * scale; + canvasRef.current.height = height * scale; const differentParent = dragParent !== widgetId; const parentDiff = { top: @@ -306,36 +341,47 @@ export function CanvasDraggingArena({ widgetId: string; isNotColliding: boolean; }[] = []; - const canvasCtx = draggingCanvas.getContext("2d"); + let canvasCtx: any = canvasRef.current.getContext("2d"); canvasCtx.globalCompositeOperation = "destination-over"; - // const drawDragLayer = () => { - // const canvas = canvasRef.current || draggingCanvas; - // const { width } = canvas.getBoundingClientRect(); + // const drawDragLayer = debounce( + // (rows) => { + // if (canvasRef.current && canvasDragLayerRef.current) { + // const { height, width } = canvasRef.current.getBoundingClientRect(); + // canvasDragLayerRef.current.width = width * scale; + // canvasDragLayerRef.current.height = height * scale; + // const canvasCtx: any = canvasDragLayerRef.current.getContext("2d"); + // canvasCtx.clearRect(0, 0, width, height); + // canvasCtx.beginPath(); // clear path if it has been used previously + // // modify method to add to path instead + // const draw = (x: any, y: any, width: any, height: any) => { + // canvasCtx.fillStyle = `${"rgb(0, 0, 0, 1)"}`; + // canvasCtx.strokeStyle = `${"rgb(0, 0, 0, 1)"}`; + // canvasCtx.rect(x, y, width, height); + // }; + // for ( + // let x = noPad ? 0 : CONTAINER_GRID_PADDING; + // x < width; + // x += snapColumnSpace + // ) { + // for ( + // let y = noPad ? 0 : CONTAINER_GRID_PADDING; + // y < rows * snapRowSpace + (widgetId === "0" ? 200 : 0); + // y += snapRowSpace + // ) { + // draw(x, y, 1, 1); + // } + // } - // canvasCtx.beginPath(); // clear path if it has been used previously - // // modify method to add to path instead - // const draw = (x: any, y: any, width: any, height: any) => { - // canvasCtx.fillStyle = `${"rgb(0, 0, 0, 1)"}`; - // canvasCtx.strokeStyle = `${"rgb(0, 0, 0, 1)"}`; - // canvasCtx.rect(x, y, width, height); - // }; - // for ( - // let x = noPad ? 0 : CONTAINER_GRID_PADDING; - // x < width; - // x += snapColumnSpace - // ) { - // for ( - // let y = noPad ? 0 : CONTAINER_GRID_PADDING; - // y < (rows + 1) * 10 - 200; - // y += snapRowSpace - // ) { - // draw(x, y, 1, 1); + // // when done, fill once + // canvasCtx.fill(); // } - // } - - // // when done, fill once - // canvasCtx.fill(); - // }; + // }, + // 0, + // { + // leading: true, + // trailing: true, + // }, + // ); // canvasCtx.scale(scale, scale); const startPoints = { @@ -347,15 +393,25 @@ export function CanvasDraggingArena({ startPoints.top = 0; setIsDragging(false); onMouseOut(); - onDrop(newRectanglesToDraw); + if (isDragging && canvasIsDragging) { + onDrop(newRectanglesToDraw); + } }; const onMouseOut = () => { - draggingCanvas.style.zIndex = null; - canvasCtx.clearRect(0, 0, width, height); - canvasIsDragging = false; + if (canvasRef.current) { + const { height, width } = canvasRef.current.getBoundingClientRect(); + canvasRef.current.style.zIndex = ""; + canvasCtx.clearRect(0, 0, width, height); + canvasIsDragging = false; + } }; const onMouseDown = (e: any) => { - if (isDragging && !canvasIsDragging) { + if ( + !isResizing && + isDragging && + !canvasIsDragging && + canvasRef.current + ) { canvasIsDragging = true; if ( dragParent === widgetId && @@ -365,13 +421,14 @@ export function CanvasDraggingArena({ startPoints.left = e.offsetX; startPoints.top = e.offsetY; } - draggingCanvas.style.zIndex = 2; + canvasRef.current.style.zIndex = "2"; + // drawDragLayer(rows); } }; const onMouseMove = (e: any) => { - if (canvasIsDragging) { + if (canvasIsDragging && canvasRef.current) { // console.log(startPoints, e.offsetX, draggingCanvas.offsetLeft); - canvasCtx.clearRect(0, 0, width, height); + const diff = { left: e.offsetX - startPoints.left - parentDiff.left, top: e.offsetY - startPoints.top - parentDiff.top, @@ -383,12 +440,18 @@ export function CanvasDraggingArena({ (each) => !selectedWidgets.includes(each.id), ) : currentOccSpaces; - newRectanglesToDraw = rectanglesToDraw.map((each) => ({ + const drawingBlocks = rectanglesToDraw.map((each) => ({ ...each, left: each.left + diff.left, top: each.top + diff.top, + })); + const newRows = updateRows(drawingBlocks, rows); + const rowDiff = newRows ? newRows - rows : 0; + rows = newRows && newRows !== rows ? newRows : rows; + newRectanglesToDraw = drawingBlocks.map((each) => ({ + ...each, isNotColliding: noCollision( - { x: each.left + diff.left, y: each.top + diff.top }, + { x: each.left, y: each.top }, snapColumnSpace, snapRowSpace, { x: 0, y: 0 }, @@ -400,28 +463,100 @@ export function CanvasDraggingArena({ GridDefaults.DEFAULT_GRID_COLUMNS, ), })); - updateRows(newRectanglesToDraw); - // const rowDiff = newRows && rows !== newRows ? newRows - rows : 0; - // rows = newRows && newRows !== rows ? newRows : rows; - // // if (rowDiff) { - // drawDragLayer(); - // // } + if (rowDiff && canvasRef.current) { + notDoneYet = true; + drawInit(rowDiff, diff); + } else if (!notDoneYet) { + const { height, width } = canvasRef.current.getBoundingClientRect(); + canvasCtx.clearRect(0, 0, width, height); + newRectanglesToDraw.forEach((each) => { + drawRectangle(each); + }); + } // if (rowDiff) { - // startPoints.top = startPoints.top + rowDiff * snapRowSpace; // newRectanglesToDraw = newRectanglesToDraw.map((each) => { - // each.top = each.top + rowDiff * snapRowSpace; - // return each; + // return { + // ...each, + // top: each.top - (rows - snapRows) * snapRowSpace, + // }; // }); + // console.log({ rowDiff, rectanglesToDraw, newRectanglesToDraw }); // } scrollToKeepUp(newRectanglesToDraw); - newRectanglesToDraw.forEach((each) => { - drawRectangle(each); - }); } else { onMouseDown(e); } }; + console.log("I am initiated again"); + let notDoneYet = false; + const drawInit = throttle( + debounce( + (rowDiff, diff) => { + console.count("drawInit"); + notDoneYet = true; + if (canvasRef.current) { + newRectanglesToDraw = rectanglesToDraw.map((each) => { + return { + ...each, + left: each.left + diff.left, + top: each.top + diff.top, + }; + }); + + canvasRef.current.height = + (rows * snapRowSpace + (widgetId === "0" ? 200 : 0)) * scale; + canvasCtx = canvasRef.current.getContext("2d"); + const { + height, + width, + } = canvasRef.current.getBoundingClientRect(); + // drawDragLayer(rows); + + canvasCtx.clearRect(0, 0, width, height); + notDoneYet = false; + newRectanglesToDraw.forEach((each) => { + drawRectangle(each); + }); + // scrollToKeepUp(newRectanglesToDraw); + } + }, + 10, + { + leading: false, + trailing: true, + }, + ), + 10, + { + leading: false, + trailing: true, + }, + ); + + // const drawInit = debounce( + // (rowDiff, diff, occSpaces) => { + // // // if (rowDiff) { + // // drawDragLayer(); + // // // } + // if (rowDiff) { + // // startPoints.top = startPoints.top + rowDiff * snapRowSpace; + // newRectanglesToDraw = rectanglesToDraw.map((each) => { + // return { + // ...each, + // top: each.top + rowDiff * snapRowSpace + // }; + // }); + // } + + // scrollToKeepUp(newRectanglesToDraw); + // }, + // 100, + // { + // leading: true, + // trailing: true, + // }, + // ); const drawRectangle = (selectionDimensions: { top: number; left: number; @@ -432,53 +567,55 @@ export function CanvasDraggingArena({ widgetId: string; isNotColliding: boolean; }) => { - const canvasCtx = - canvasRef.current?.getContext("2d") || - draggingCanvas.getContext("2d"); - const snappedXY = getSnappedXY( - snapColumnSpace, - snapRowSpace, - { - x: selectionDimensions.left, - y: selectionDimensions.top, - }, - { - x: 0, - y: 0, - }, - ); + if (canvasRef.current) { + const canvasCtx: any = canvasRef.current.getContext("2d"); + const snappedXY = getSnappedXY( + snapColumnSpace, + snapRowSpace, + { + x: selectionDimensions.left, + y: selectionDimensions.top, + }, + { + x: 0, + y: 0, + }, + ); - canvasCtx.fillStyle = `${ - selectionDimensions.isNotColliding ? "rgb(104, 113, 239, 0.6)" : "red" - }`; - canvasCtx.fillRect( - selectionDimensions.left + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.top + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.width, - selectionDimensions.height, - ); - canvasCtx.fillStyle = `${ - selectionDimensions.isNotColliding ? "rgb(233, 250, 243, 0.6)" : "red" - }`; - const strokeWidth = 1; - canvasCtx.setLineDash([3]); - canvasCtx.strokeStyle = "rgb(104, 113, 239)"; - canvasCtx.strokeRect( - snappedXY.X + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), - snappedXY.Y + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.width - strokeWidth, - selectionDimensions.height - strokeWidth, - ); + canvasCtx.fillStyle = `${ + selectionDimensions.isNotColliding ? "rgb(104, 113, 239, 0.6)" : "red" + }`; + canvasCtx.fillRect( + selectionDimensions.left + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.top + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.width, + selectionDimensions.height, + ); + canvasCtx.fillStyle = `${ + selectionDimensions.isNotColliding + ? "rgb(233, 250, 243, 0.6)" + : "red" + }`; + const strokeWidth = 1; + canvasCtx.setLineDash([3]); + canvasCtx.strokeStyle = "rgb(104, 113, 239)"; + canvasCtx.strokeRect( + snappedXY.X + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), + snappedXY.Y + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.width - strokeWidth, + selectionDimensions.height - strokeWidth, + ); + } }; const startDragging = () => { - draggingCanvas.addEventListener("mousemove", onMouseMove, false); - draggingCanvas.addEventListener("mouseup", onMouseUp, false); - draggingCanvas.addEventListener("mouseover", onMouseDown, false); - draggingCanvas.addEventListener("mouseout", onMouseOut, false); + canvasRef.current?.addEventListener("mousemove", onMouseMove, false); + canvasRef.current?.addEventListener("mouseup", onMouseUp, false); + canvasRef.current?.addEventListener("mouseover", onMouseDown, false); + canvasRef.current?.addEventListener("mouseout", onMouseOut, false); if (canvasIsDragging) { // fix_dpi(); - // drawDragLayer(); + // drawDragLayer(rows); rectanglesToDraw.forEach((each) => { drawRectangle(each); }); @@ -486,17 +623,17 @@ export function CanvasDraggingArena({ }; startDragging(); if (dragParent === widgetId) { - draggingCanvas.style.zIndex = 2; + canvasRef.current.style.zIndex = "2"; } return () => { - draggingCanvas.removeEventListener("mousemove", onMouseMove); - draggingCanvas.removeEventListener("mouseup", onMouseUp); - draggingCanvas.removeEventListener("mouseenter", onMouseDown); - draggingCanvas.removeEventListener("mouseleave", onMouseOut); + canvasRef.current?.removeEventListener("mousemove", onMouseMove); + canvasRef.current?.removeEventListener("mouseup", onMouseUp); + canvasRef.current?.removeEventListener("mouseenter", onMouseDown); + canvasRef.current?.removeEventListener("mouseleave", onMouseOut); }; } - }, [isDragging]); - return isDragging ? ( + }, [isDragging, newWidget, isResizing]); + return isDragging && !isResizing ? ( ) : null; } +CanvasDraggingArena.whyDidYouRender = { + logOnDifferentValues: true, +}; CanvasDraggingArena.displayName = "CanvasDraggingArena"; diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 84e19a2c60a2..48504174701b 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -79,7 +79,7 @@ export const CanvasSelectionArena = memo( const selectionCanvas: any = document.getElementById( `canvas-${widgetId}`, ); - const scale = window.devicePixelRatio; + const scale = 1; const canvasCtx = selectionCanvas.getContext("2d"); const initRectangle = (): SelectedArenaDimensions => ({ diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index 0385f4f81ef7..cf32b9a38d7d 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -8,6 +8,7 @@ const initialState: WidgetDragResizeState = { isResizing: false, dragParent: "", dragCenter: "", + newWidget: undefined, lastSelectedWidget: undefined, selectedWidgets: [], focusedWidget: undefined, @@ -33,6 +34,16 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { state.isDragging = action.payload.isDragging; state.dragCenter = action.payload.dragCenter; }, + [ReduxActionTypes.SET_NEW_WIDGET_DRAGGING_2]: ( + state: WidgetDragResizeState, + action: ReduxAction<{ + isDragging: boolean; + widget: string; + }>, + ) => { + state.isDragging = action.payload.isDragging; + state.newWidget = action.payload.widget; + }, [ReduxActionTypes.SET_WIDGET_DRAGGING]: ( state: WidgetDragResizeState, action: ReduxAction<{ isDragging: boolean }>, @@ -130,6 +141,7 @@ export type WidgetDragResizeState = { focusedWidget?: string; selectedWidgetAncestory: string[]; selectedWidgets: string[]; + newWidget: any; }; export default widgetDraggingReducer; diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index 3d9c2f6810df..eab0ce2305bd 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -51,7 +51,7 @@ export const Directions: { [id: string]: string } = { }; export type Direction = typeof Directions[keyof typeof Directions]; -const SCROLL_THESHOLD = 10; +const SCROLL_THESHOLD = 2.5; export const getScrollByPixels = function( elem: Element, diff --git a/app/client/src/utils/hooks/dragResizeHooks.tsx b/app/client/src/utils/hooks/dragResizeHooks.tsx index 14aecd9902a6..25d87ce6ed99 100644 --- a/app/client/src/utils/hooks/dragResizeHooks.tsx +++ b/app/client/src/utils/hooks/dragResizeHooks.tsx @@ -109,6 +109,20 @@ export const useWidgetDragResize = () => { }, [dispatch], ), + setDraggingNewWidget: useCallback( + (isDragging: boolean, widget: any) => { + if (isDragging) { + document.body.classList.add("dragging"); + } else { + document.body.classList.remove("dragging"); + } + dispatch({ + type: ReduxActionTypes.SET_NEW_WIDGET_DRAGGING_2, + payload: { isDragging, widget }, + }); + }, + [dispatch], + ), setDragItemsInitialParent: useCallback( (isDragging: boolean, dragParent: string, dragCenter: string) => { if (isDragging) { From 84308cec59834620f8715a69512c3b7ae794abb1 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 2 Jul 2021 01:12:40 +0530 Subject: [PATCH 03/68] scroll --- .../src/pages/common/CanvasDraggingArena.tsx | 29 +++++++++++++++---- app/client/src/utils/helpers.tsx | 10 ------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 3459da821131..29bb4137aa88 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -174,19 +174,36 @@ export function CanvasDraggingArena({ }[], ) => { if (isDragging) { - const sortedByTopBlocks = drawingBlocks.sort( - (each1, each2) => each2.top + each2.height - (each1.top + each1.height), - ); - const bottomMostBlock = sortedByTopBlocks[0]; + let groupBlock; + if (drawingBlocks.length > 1) { + const sortedByTopBlocks = drawingBlocks.sort( + (each1, each2) => + each2.top + each2.height - (each1.top + each1.height), + ); + const bottomMostBlock = sortedByTopBlocks[0]; + const topMostBlock = sortedByTopBlocks[sortedByTopBlocks.length - 1]; + groupBlock = { + top: topMostBlock.top, + height: + topMostBlock.top + bottomMostBlock.top + bottomMostBlock.height, + }; + } else { + const block = drawingBlocks[0]; + groupBlock = { + top: block.top, + height: block.height, + }; + } + // const el = canvasRef?.current; const scrollParent: Element | null = getNearestParentCanvas( canvasRef.current, ); if (canvasRef.current) { // if (el && props.canDropTargetExtend) { - if (bottomMostBlock) { + if (groupBlock) { scrollElementIntoParentCanvasView2( - bottomMostBlock, + groupBlock, scrollParent, canvasRef.current, ); diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index eab0ce2305bd..ec6149a55116 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -74,13 +74,8 @@ export const getScrollByPixels = function( export const getScrollByPixels2 = function( elem: { - left: number; top: number; - width: number; height: number; - columnWidth: number; - rowHeight: number; - widgetId: string; }, scrollParent: Element, child: Element, @@ -129,13 +124,8 @@ export const scrollElementIntoParentCanvasView = ( export const scrollElementIntoParentCanvasView2 = ( el: { - left: number; top: number; - width: number; height: number; - columnWidth: number; - rowHeight: number; - widgetId: string; } | null, parent: Element | null, child: Element | null, From e27cf78d227c48d14db4caa041dfbb657f4f9aff Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 2 Jul 2021 11:52:15 +0530 Subject: [PATCH 04/68] fixes --- .../appsmith/PositionedContainer.tsx | 6 +- .../editorComponents/DraggableComponent.tsx | 7 +- app/client/src/pages/Editor/WidgetCard.tsx | 2 +- .../src/pages/common/CanvasDraggingArena.tsx | 84 +++++++++++-------- 4 files changed, 53 insertions(+), 46 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index fa03df4b993c..8f6671f81200 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -47,9 +47,6 @@ export function PositionedContainer(props: PositionedContainerProps) { const isDragging = useSelector( (state: AppState) => state.ui.widgetDragResize.isDragging, ); - const isResizing = useSelector( - (state: AppState) => state.ui.widgetDragResize.isDragging, - ); const selectedWidgets = useSelector(getSelectedWidgets); const isThisWidgetDragging = isDragging && selectedWidgets.includes(props.widgetId); @@ -63,7 +60,6 @@ export function PositionedContainer(props: PositionedContainerProps) { padding: padding + "px", zIndex: isDragging && - !isResizing && !isThisWidgetDragging && [ "CONTAINER_WIDGET", @@ -77,7 +73,7 @@ export function PositionedContainer(props: PositionedContainerProps) { : Layers.positionedWidget, backgroundColor: "inherit", }; - }, [props.style, isDragging, isResizing]); + }, [props.style, isDragging]); const openPropPane = useCallback((e) => openPropertyPane(e, props.widgetId), [ props.widgetId, diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 82e5a205336a..96c699c81acb 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -217,11 +217,8 @@ function DraggableComponent(props: DraggableComponentProps) { }} onMouseMove={mouseMove} onMouseOver={handleMouseOver} - onMouseUp={(e) => { - if (!isResizingOrDragging) { - setMightBeDragging(false); - e.stopPropagation(); - } + onMouseUp={() => { + setMightBeDragging(false); }} style={style} > diff --git a/app/client/src/pages/Editor/WidgetCard.tsx b/app/client/src/pages/Editor/WidgetCard.tsx index e70bf9a86d38..be5aa1dee1e7 100644 --- a/app/client/src/pages/Editor/WidgetCard.tsx +++ b/app/client/src/pages/Editor/WidgetCard.tsx @@ -95,7 +95,7 @@ function WidgetCard(props: CardProps) { }); // We've finished dragging, generate a new widgetId to be used for next drag. setWidgetId(generateReactKey()); - // setDraggingNewWidget && setDraggingNewWidget(false, undefined); + setDraggingNewWidget && setDraggingNewWidget(false, undefined); }, }); diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 29bb4137aa88..263045e7784d 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -156,7 +156,8 @@ export function CanvasDraggingArena({ isNotColliding: true, }, ]; - const { setIsDragging } = useWidgetDragResize(); + const { setDraggingNewWidget, setIsDragging } = useWidgetDragResize(); + const canvasRef = React.useRef(null); const { persistDropTargetRows, updateDropTargetRows } = useContext( DropTargetContext, @@ -177,15 +178,18 @@ export function CanvasDraggingArena({ let groupBlock; if (drawingBlocks.length > 1) { const sortedByTopBlocks = drawingBlocks.sort( + (each2, each1) => each2.top - each1.top, + ); + const topMostBlock = sortedByTopBlocks[0]; + const sortedByBottomBlocks = drawingBlocks.sort( (each1, each2) => each2.top + each2.height - (each1.top + each1.height), ); - const bottomMostBlock = sortedByTopBlocks[0]; - const topMostBlock = sortedByTopBlocks[sortedByTopBlocks.length - 1]; + const bottomMostBlock = sortedByBottomBlocks[0]; groupBlock = { top: topMostBlock.top, height: - topMostBlock.top + bottomMostBlock.top + bottomMostBlock.height, + bottomMostBlock.top - topMostBlock.top + bottomMostBlock.height, }; } else { const block = drawingBlocks[0]; @@ -292,32 +296,38 @@ export function CanvasDraggingArena({ return !each.isNotColliding; }); if (!cannotDrop) { - drawingBlocks.forEach((each) => { - const widget = newWidget ? newWidget : allWidgets[each.widgetId]; - const updateWidgetParams = widgetOperationParams( - widget, - { x: each.left, y: each.top }, - { x: 0, y: 0 }, - snapColumnSpace, - snapRowSpace, - widget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : widgetId, - ); + drawingBlocks + .sort( + (each1, each2) => + each1.top + each1.height - (each2.top + each2.height), + ) + .forEach((each) => { + const widget = newWidget ? newWidget : allWidgets[each.widgetId]; + const updateWidgetParams = widgetOperationParams( + widget, + { x: each.left, y: each.top }, + { x: 0, y: 0 }, + snapColumnSpace, + snapRowSpace, + widget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : widgetId, + ); - // const widgetBottomRow = getWidgetBottomRow(widget, updateWidgetParams); - const widgetBottomRow = - updateWidgetParams.payload.topRow + - (updateWidgetParams.payload.rows || widget.bottomRow - widget.topRow); - persistDropTargetRows && - persistDropTargetRows(widget.widgetId, widgetBottomRow); + // const widgetBottomRow = getWidgetBottomRow(widget, updateWidgetParams); + const widgetBottomRow = + updateWidgetParams.payload.topRow + + (updateWidgetParams.payload.rows || + widget.bottomRow - widget.topRow); + persistDropTargetRows && + persistDropTargetRows(widget.widgetId, widgetBottomRow); - /* Finally update the widget */ - updateWidget && - updateWidget( - updateWidgetParams.operation, - updateWidgetParams.widgetId, - updateWidgetParams.payload, - ); - }); + /* Finally update the widget */ + updateWidget && + updateWidget( + updateWidgetParams.operation, + updateWidgetParams.widgetId, + updateWidgetParams.payload, + ); + }); } }; @@ -402,13 +412,17 @@ export function CanvasDraggingArena({ // canvasCtx.scale(scale, scale); const startPoints = { - left: 0, - top: 0, + left: 20, + top: 20, }; const onMouseUp = () => { - startPoints.left = 0; - startPoints.top = 0; - setIsDragging(false); + startPoints.left = 20; + startPoints.top = 20; + if (newWidget) { + setDraggingNewWidget(false, undefined); + } else { + setIsDragging(false); + } onMouseOut(); if (isDragging && canvasIsDragging) { onDrop(newRectanglesToDraw); @@ -432,8 +446,8 @@ export function CanvasDraggingArena({ canvasIsDragging = true; if ( dragParent === widgetId && - startPoints.left === 0 && - startPoints.top === 0 + startPoints.left === 20 && + startPoints.top === 20 ) { startPoints.left = e.offsetX; startPoints.top = e.offsetY; From 2a1d396621bd21d99e0d5b8585a81d352da001c0 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 2 Jul 2021 16:52:38 +0530 Subject: [PATCH 05/68] dip --- .../src/actions/canvasSelectionActions.ts | 8 +- .../appsmith/PositionedContainer.tsx | 2 +- .../editorComponents/DraggableComponent.tsx | 2 +- .../src/pages/common/CanvasDraggingArena.tsx | 612 +++++++++--------- .../src/pages/common/CanvasSelectionArena.tsx | 13 +- app/client/src/sagas/SelectionCanvasSagas.ts | 26 +- 6 files changed, 348 insertions(+), 315 deletions(-) diff --git a/app/client/src/actions/canvasSelectionActions.ts b/app/client/src/actions/canvasSelectionActions.ts index e9030b14e2ee..9e76953b4fb4 100644 --- a/app/client/src/actions/canvasSelectionActions.ts +++ b/app/client/src/actions/canvasSelectionActions.ts @@ -1,11 +1,17 @@ import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; import { SelectedArenaDimensions } from "pages/common/CanvasSelectionArena"; -export const setCanvasSelectionStateAction = (start: boolean) => { +export const setCanvasSelectionStateAction = ( + start: boolean, + widgetId: string, +) => { return { type: start ? ReduxActionTypes.START_CANVAS_SELECTION : ReduxActionTypes.STOP_CANVAS_SELECTION, + payload: { + widgetId, + }, }; }; diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index 8f6671f81200..91576bf1b1df 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -86,7 +86,7 @@ export function PositionedContainer(props: PositionedContainerProps) { data-testid="test-widget" id={props.widgetId} key={`positioned-container-${props.widgetId}`} - isDragging + isDragging={isDragging} onClick={stopEventPropagation} // Positioned Widget is the top enclosure for all widgets and clicks on/inside the widget should not be propogated/bubbled out of this Container. onClickCapture={openPropPane} diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 96c699c81acb..5bd997763483 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -210,7 +210,7 @@ function DraggableComponent(props: DraggableComponentProps) { { - if (!isResizingOrDragging) { + if (!e.metaKey && !isResizingOrDragging) { setMightBeDragging(true); e.stopPropagation(); } diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 263045e7784d..b250686fdd42 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -332,336 +332,352 @@ export function CanvasDraggingArena({ }; useEffect(() => { - if (isDragging && canvasRef.current && !isResizing) { - let rows = snapRows; - let canvasIsDragging = false; + if (canvasRef.current && !isResizing) { const scale = 1; - // draggingCanvas.style.padding = `${noPad ? 0 : CONTAINER_GRID_PADDING}px`; - const { height, width } = canvasRef.current.getBoundingClientRect(); - canvasRef.current.width = width * scale; - canvasRef.current.height = height * scale; - const differentParent = dragParent !== widgetId; - const parentDiff = { - top: - differentParent && dragCenterSpace - ? dragCenterSpace.top * snapRowSpace + - (noPad ? 0 : CONTAINER_GRID_PADDING) - : noPad - ? 0 - : CONTAINER_GRID_PADDING, - left: - differentParent && dragCenterSpace - ? dragCenterSpace.left * snapColumnSpace + - (noPad ? 0 : CONTAINER_GRID_PADDING) - : noPad - ? 0 - : CONTAINER_GRID_PADDING, - }; - let newRectanglesToDraw: { - top: number; - left: number; - width: number; - height: number; - columnWidth: number; - rowHeight: number; - widgetId: string; - isNotColliding: boolean; - }[] = []; - let canvasCtx: any = canvasRef.current.getContext("2d"); - canvasCtx.globalCompositeOperation = "destination-over"; - // const drawDragLayer = debounce( - // (rows) => { - // if (canvasRef.current && canvasDragLayerRef.current) { - // const { height, width } = canvasRef.current.getBoundingClientRect(); - // canvasDragLayerRef.current.width = width * scale; - // canvasDragLayerRef.current.height = height * scale; - // const canvasCtx: any = canvasDragLayerRef.current.getContext("2d"); - // canvasCtx.clearRect(0, 0, width, height); - // canvasCtx.beginPath(); // clear path if it has been used previously - // // modify method to add to path instead - // const draw = (x: any, y: any, width: any, height: any) => { - // canvasCtx.fillStyle = `${"rgb(0, 0, 0, 1)"}`; - // canvasCtx.strokeStyle = `${"rgb(0, 0, 0, 1)"}`; - // canvasCtx.rect(x, y, width, height); - // }; - // for ( - // let x = noPad ? 0 : CONTAINER_GRID_PADDING; - // x < width; - // x += snapColumnSpace - // ) { - // for ( - // let y = noPad ? 0 : CONTAINER_GRID_PADDING; - // y < rows * snapRowSpace + (widgetId === "0" ? 200 : 0); - // y += snapRowSpace - // ) { - // draw(x, y, 1, 1); - // } - // } - - // // when done, fill once - // canvasCtx.fill(); - // } - // }, - // 0, - // { - // leading: true, - // trailing: true, - // }, - // ); - // canvasCtx.scale(scale, scale); - - const startPoints = { - left: 20, - top: 20, - }; - const onMouseUp = () => { - startPoints.left = 20; - startPoints.top = 20; - if (newWidget) { - setDraggingNewWidget(false, undefined); - } else { - setIsDragging(false); - } - onMouseOut(); - if (isDragging && canvasIsDragging) { - onDrop(newRectanglesToDraw); - } - }; + let canvasIsDragging = false; const onMouseOut = () => { if (canvasRef.current) { const { height, width } = canvasRef.current.getBoundingClientRect(); + const canvasCtx: any = canvasRef.current.getContext("2d"); canvasRef.current.style.zIndex = ""; - canvasCtx.clearRect(0, 0, width, height); + canvasCtx.clearRect(0, 0, width * scale, height * scale); canvasIsDragging = false; } }; - const onMouseDown = (e: any) => { - if ( - !isResizing && - isDragging && - !canvasIsDragging && - canvasRef.current - ) { - canvasIsDragging = true; - if ( - dragParent === widgetId && - startPoints.left === 20 && - startPoints.top === 20 - ) { - startPoints.left = e.offsetX; - startPoints.top = e.offsetY; - } - canvasRef.current.style.zIndex = "2"; - // drawDragLayer(rows); - } - }; - const onMouseMove = (e: any) => { - if (canvasIsDragging && canvasRef.current) { - // console.log(startPoints, e.offsetX, draggingCanvas.offsetLeft); + if (isDragging) { + let rows = snapRows; - const diff = { - left: e.offsetX - startPoints.left - parentDiff.left, - top: e.offsetY - startPoints.top - parentDiff.top, - }; - const currentOccSpaces = occupiedSpaces[widgetId]; - const occSpaces: OccupiedSpace[] = - dragParent === widgetId - ? childrenOccupiedSpaces.filter( - (each) => !selectedWidgets.includes(each.id), - ) - : currentOccSpaces; - const drawingBlocks = rectanglesToDraw.map((each) => ({ - ...each, - left: each.left + diff.left, - top: each.top + diff.top, - })); - const newRows = updateRows(drawingBlocks, rows); - const rowDiff = newRows ? newRows - rows : 0; - rows = newRows && newRows !== rows ? newRows : rows; - newRectanglesToDraw = drawingBlocks.map((each) => ({ - ...each, - isNotColliding: noCollision( - { x: each.left, y: each.top }, - snapColumnSpace, - snapRowSpace, - { x: 0, y: 0 }, - each.columnWidth, - each.rowHeight, - each.widgetId, - occSpaces, - rows, - GridDefaults.DEFAULT_GRID_COLUMNS, - ), - })); - if (rowDiff && canvasRef.current) { - notDoneYet = true; - drawInit(rowDiff, diff); - } else if (!notDoneYet) { - const { height, width } = canvasRef.current.getBoundingClientRect(); - canvasCtx.clearRect(0, 0, width, height); - newRectanglesToDraw.forEach((each) => { - drawRectangle(each); - }); + // draggingCanvas.style.padding = `${noPad ? 0 : CONTAINER_GRID_PADDING}px`; + const { height, width } = canvasRef.current.getBoundingClientRect(); + canvasRef.current.width = width * scale; + canvasRef.current.height = height * scale; + + const differentParent = dragParent !== widgetId; + const parentDiff = { + top: + differentParent && dragCenterSpace + ? dragCenterSpace.top * snapRowSpace + + (noPad ? 0 : CONTAINER_GRID_PADDING) + : noPad + ? 0 + : CONTAINER_GRID_PADDING, + left: + differentParent && dragCenterSpace + ? dragCenterSpace.left * snapColumnSpace + + (noPad ? 0 : CONTAINER_GRID_PADDING) + : noPad + ? 0 + : CONTAINER_GRID_PADDING, + }; + let newRectanglesToDraw: { + top: number; + left: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + isNotColliding: boolean; + }[] = []; + let canvasCtx: any = canvasRef.current.getContext("2d"); + canvasCtx.globalCompositeOperation = "destination-over"; + canvasCtx.scale(scale, scale); + // const drawDragLayer = debounce( + // (rows) => { + // if (canvasRef.current && canvasDragLayerRef.current) { + // const { height, width } = canvasRef.current.getBoundingClientRect(); + // canvasDragLayerRef.current.width = width * scale; + // canvasDragLayerRef.current.height = height * scale; + // const canvasCtx: any = canvasDragLayerRef.current.getContext("2d"); + // canvasCtx.clearRect(0, 0, width, height); + // canvasCtx.beginPath(); // clear path if it has been used previously + // // modify method to add to path instead + // const draw = (x: any, y: any, width: any, height: any) => { + // canvasCtx.fillStyle = `${"rgb(0, 0, 0, 1)"}`; + // canvasCtx.strokeStyle = `${"rgb(0, 0, 0, 1)"}`; + // canvasCtx.rect(x, y, width, height); + // }; + // for ( + // let x = noPad ? 0 : CONTAINER_GRID_PADDING; + // x < width; + // x += snapColumnSpace + // ) { + // for ( + // let y = noPad ? 0 : CONTAINER_GRID_PADDING; + // y < rows * snapRowSpace + (widgetId === "0" ? 200 : 0); + // y += snapRowSpace + // ) { + // draw(x, y, 1, 1); + // } + // } + + // // when done, fill once + // canvasCtx.fill(); + // } + // }, + // 0, + // { + // leading: true, + // trailing: true, + // }, + // ); + + const startPoints = { + left: 20, + top: 20, + }; + const onMouseUp = () => { + startPoints.left = 20; + startPoints.top = 20; + if (newWidget) { + setDraggingNewWidget(false, undefined); + } else { + setIsDragging(false); + } + onMouseOut(); + if (isDragging && canvasIsDragging) { + onDrop(newRectanglesToDraw); } - // if (rowDiff) { - // newRectanglesToDraw = newRectanglesToDraw.map((each) => { - // return { - // ...each, - // top: each.top - (rows - snapRows) * snapRowSpace, - // }; - // }); - // console.log({ rowDiff, rectanglesToDraw, newRectanglesToDraw }); - // } + }; - scrollToKeepUp(newRectanglesToDraw); - } else { - onMouseDown(e); - } - }; - console.log("I am initiated again"); - let notDoneYet = false; - const drawInit = throttle( - debounce( - (rowDiff, diff) => { - console.count("drawInit"); - notDoneYet = true; - if (canvasRef.current) { - newRectanglesToDraw = rectanglesToDraw.map((each) => { - return { - ...each, - left: each.left + diff.left, - top: each.top + diff.top, - }; - }); + const onMouseDown = (e: any) => { + if ( + !isResizing && + isDragging && + !canvasIsDragging && + canvasRef.current + ) { + canvasIsDragging = true; + if ( + dragParent === widgetId && + startPoints.left === 20 && + startPoints.top === 20 + ) { + startPoints.left = e.offsetX; + startPoints.top = e.offsetY; + } + canvasRef.current.style.zIndex = "2"; + // drawDragLayer(rows); + } + }; + const onMouseMove = (e: any) => { + if (canvasIsDragging && canvasRef.current) { + // console.log(startPoints, e.offsetX, draggingCanvas.offsetLeft); - canvasRef.current.height = - (rows * snapRowSpace + (widgetId === "0" ? 200 : 0)) * scale; - canvasCtx = canvasRef.current.getContext("2d"); + const diff = { + left: e.offsetX - startPoints.left - parentDiff.left, + top: e.offsetY - startPoints.top - parentDiff.top, + }; + const currentOccSpaces = occupiedSpaces[widgetId]; + const occSpaces: OccupiedSpace[] = + dragParent === widgetId + ? childrenOccupiedSpaces.filter( + (each) => !selectedWidgets.includes(each.id), + ) + : currentOccSpaces; + const drawingBlocks = rectanglesToDraw.map((each) => ({ + ...each, + left: each.left + diff.left, + top: each.top + diff.top, + })); + const newRows = updateRows(drawingBlocks, rows); + const rowDiff = newRows ? newRows - rows : 0; + rows = newRows && newRows !== rows ? newRows : rows; + newRectanglesToDraw = drawingBlocks.map((each) => ({ + ...each, + isNotColliding: noCollision( + { x: each.left, y: each.top }, + snapColumnSpace, + snapRowSpace, + { x: 0, y: 0 }, + each.columnWidth, + each.rowHeight, + each.widgetId, + occSpaces, + rows, + GridDefaults.DEFAULT_GRID_COLUMNS, + ), + })); + if (rowDiff && canvasRef.current) { + notDoneYet = true; + drawInit(rowDiff, diff); + } else if (!notDoneYet) { const { height, width, } = canvasRef.current.getBoundingClientRect(); - // drawDragLayer(rows); - - canvasCtx.clearRect(0, 0, width, height); - notDoneYet = false; + canvasCtx.clearRect(0, 0, width * scale, height * scale); newRectanglesToDraw.forEach((each) => { drawRectangle(each); }); - // scrollToKeepUp(newRectanglesToDraw); } - }, + // if (rowDiff) { + // newRectanglesToDraw = newRectanglesToDraw.map((each) => { + // return { + // ...each, + // top: each.top - (rows - snapRows) * snapRowSpace, + // }; + // }); + // console.log({ rowDiff, rectanglesToDraw, newRectanglesToDraw }); + // } + + scrollToKeepUp(newRectanglesToDraw); + } else { + onMouseDown(e); + } + }; + console.log("I am initiated again"); + let notDoneYet = false; + const drawInit = throttle( + debounce( + (rowDiff, diff) => { + console.count("drawInit"); + notDoneYet = true; + if (canvasRef.current) { + newRectanglesToDraw = rectanglesToDraw.map((each) => { + return { + ...each, + left: each.left + diff.left, + top: each.top + diff.top, + }; + }); + + canvasRef.current.height = + rows * snapRowSpace + (widgetId === "0" ? 200 : 0) * scale; + canvasCtx = canvasRef.current.getContext("2d"); + canvasCtx.scale(scale, scale); + const { + height, + width, + } = canvasRef.current.getBoundingClientRect(); + // drawDragLayer(rows); + + canvasCtx.clearRect(0, 0, width * scale, height * scale); + notDoneYet = false; + newRectanglesToDraw.forEach((each) => { + drawRectangle(each); + }); + // scrollToKeepUp(newRectanglesToDraw); + } + }, + 10, + { + leading: false, + trailing: true, + }, + ), 10, { leading: false, trailing: true, }, - ), - 10, - { - leading: false, - trailing: true, - }, - ); + ); - // const drawInit = debounce( - // (rowDiff, diff, occSpaces) => { - // // // if (rowDiff) { - // // drawDragLayer(); - // // // } - // if (rowDiff) { - // // startPoints.top = startPoints.top + rowDiff * snapRowSpace; - // newRectanglesToDraw = rectanglesToDraw.map((each) => { - // return { - // ...each, - // top: each.top + rowDiff * snapRowSpace - // }; - // }); - // } + // const drawInit = debounce( + // (rowDiff, diff, occSpaces) => { + // // // if (rowDiff) { + // // drawDragLayer(); + // // // } + // if (rowDiff) { + // // startPoints.top = startPoints.top + rowDiff * snapRowSpace; + // newRectanglesToDraw = rectanglesToDraw.map((each) => { + // return { + // ...each, + // top: each.top + rowDiff * snapRowSpace + // }; + // }); + // } - // scrollToKeepUp(newRectanglesToDraw); - // }, - // 100, - // { - // leading: true, - // trailing: true, - // }, - // ); - const drawRectangle = (selectionDimensions: { - top: number; - left: number; - width: number; - height: number; - columnWidth: number; - rowHeight: number; - widgetId: string; - isNotColliding: boolean; - }) => { - if (canvasRef.current) { - const canvasCtx: any = canvasRef.current.getContext("2d"); - const snappedXY = getSnappedXY( - snapColumnSpace, - snapRowSpace, - { - x: selectionDimensions.left, - y: selectionDimensions.top, - }, - { - x: 0, - y: 0, - }, - ); + // scrollToKeepUp(newRectanglesToDraw); + // }, + // 100, + // { + // leading: true, + // trailing: true, + // }, + // ); + const drawRectangle = (selectionDimensions: { + top: number; + left: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + isNotColliding: boolean; + }) => { + if (canvasRef.current) { + const canvasCtx: any = canvasRef.current.getContext("2d"); + const snappedXY = getSnappedXY( + snapColumnSpace, + snapRowSpace, + { + x: selectionDimensions.left, + y: selectionDimensions.top, + }, + { + x: 0, + y: 0, + }, + ); - canvasCtx.fillStyle = `${ - selectionDimensions.isNotColliding ? "rgb(104, 113, 239, 0.6)" : "red" - }`; - canvasCtx.fillRect( - selectionDimensions.left + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.top + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.width, - selectionDimensions.height, - ); - canvasCtx.fillStyle = `${ - selectionDimensions.isNotColliding - ? "rgb(233, 250, 243, 0.6)" - : "red" - }`; - const strokeWidth = 1; - canvasCtx.setLineDash([3]); - canvasCtx.strokeStyle = "rgb(104, 113, 239)"; - canvasCtx.strokeRect( - snappedXY.X + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), - snappedXY.Y + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.width - strokeWidth, - selectionDimensions.height - strokeWidth, - ); - } - }; - const startDragging = () => { - canvasRef.current?.addEventListener("mousemove", onMouseMove, false); - canvasRef.current?.addEventListener("mouseup", onMouseUp, false); - canvasRef.current?.addEventListener("mouseover", onMouseDown, false); - canvasRef.current?.addEventListener("mouseout", onMouseOut, false); + canvasCtx.fillStyle = `${ + selectionDimensions.isNotColliding + ? "rgb(104, 113, 239, 0.6)" + : "red" + }`; + canvasCtx.fillRect( + selectionDimensions.left + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.top + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.width, + selectionDimensions.height, + ); + canvasCtx.fillStyle = `${ + selectionDimensions.isNotColliding + ? "rgb(233, 250, 243, 0.6)" + : "red" + }`; + const strokeWidth = 1; + canvasCtx.setLineDash([3]); + canvasCtx.strokeStyle = "rgb(104, 113, 239)"; + canvasCtx.strokeRect( + snappedXY.X + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), + snappedXY.Y + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.width - strokeWidth, + selectionDimensions.height - strokeWidth, + ); + } + }; + const startDragging = () => { + canvasRef.current?.addEventListener("mousemove", onMouseMove, false); + canvasRef.current?.addEventListener("mouseup", onMouseUp, false); + canvasRef.current?.addEventListener("mouseover", onMouseDown, false); + canvasRef.current?.addEventListener("mouseout", onMouseOut, false); + canvasRef.current?.addEventListener("mouseleave", onMouseOut, false); - if (canvasIsDragging) { - // fix_dpi(); - // drawDragLayer(rows); - rectanglesToDraw.forEach((each) => { - drawRectangle(each); - }); + if (canvasIsDragging) { + // fix_dpi(); + // drawDragLayer(rows); + rectanglesToDraw.forEach((each) => { + drawRectangle(each); + }); + } + }; + startDragging(); + if (dragParent === widgetId) { + canvasRef.current.style.zIndex = "2"; } - }; - startDragging(); - if (dragParent === widgetId) { - canvasRef.current.style.zIndex = "2"; + return () => { + canvasRef.current?.removeEventListener("mousemove", onMouseMove); + canvasRef.current?.removeEventListener("mouseup", onMouseUp); + canvasRef.current?.removeEventListener("mouseover", onMouseDown); + canvasRef.current?.removeEventListener("mouseout", onMouseOut); + canvasRef.current?.removeEventListener("mouseleave", onMouseOut); + }; + } else { + onMouseOut(); } - return () => { - canvasRef.current?.removeEventListener("mousemove", onMouseMove); - canvasRef.current?.removeEventListener("mouseup", onMouseUp); - canvasRef.current?.removeEventListener("mouseenter", onMouseDown); - canvasRef.current?.removeEventListener("mouseleave", onMouseOut); - }; } }, [isDragging, newWidget, isResizing]); return isDragging && !isResizing ? ( diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 48504174701b..83e6aa1d9706 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -2,8 +2,6 @@ import { selectAllWidgetsInAreaAction, setCanvasSelectionStateAction, } from "actions/canvasSelectionActions"; -import { theme } from "constants/DefaultTheme"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { throttle } from "lodash"; import React, { memo, useEffect, useCallback } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -45,7 +43,7 @@ export const CanvasSelectionArena = memo( (state: AppState) => state.ui.widgetDragResize.isDragging, ); const mainContainer = useSelector((state: AppState) => - getWidget(state, MAIN_CONTAINER_WIDGET_ID), + getWidget(state, widgetId), ); const currentPageId = useSelector(getCurrentPageId); const appLayout = useSelector(getCurrentApplicationLayout); @@ -95,9 +93,8 @@ export const CanvasSelectionArena = memo( const init = () => { const { height, width } = selectionCanvas.getBoundingClientRect(); if (height && width) { - selectionCanvas.width = mainContainer.rightColumn * scale; - selectionCanvas.height = - (mainContainer.bottomRow + theme.canvasBottomPadding) * scale; + selectionCanvas.width = width; + selectionCanvas.height = height; } canvasCtx.scale(scale, scale); selectionCanvas.addEventListener("click", onClick, false); @@ -196,7 +193,7 @@ export const CanvasSelectionArena = memo( selectionRectangle.width = 0; selectionRectangle.height = 0; isDragging = true; - dispatch(setCanvasSelectionStateAction(true)); + dispatch(setCanvasSelectionStateAction(true, widgetId)); // bring the canvas to the top layer selectionCanvas.style.zIndex = 2; }; @@ -210,7 +207,7 @@ export const CanvasSelectionArena = memo( selectionCanvas.height, ); selectionCanvas.style.zIndex = null; - dispatch(setCanvasSelectionStateAction(false)); + dispatch(setCanvasSelectionStateAction(false, widgetId)); } }; const onMouseMove = (e: any) => { diff --git a/app/client/src/sagas/SelectionCanvasSagas.ts b/app/client/src/sagas/SelectionCanvasSagas.ts index d464068bfe7b..7d973639076f 100644 --- a/app/client/src/sagas/SelectionCanvasSagas.ts +++ b/app/client/src/sagas/SelectionCanvasSagas.ts @@ -3,9 +3,9 @@ import { OccupiedSpace } from "constants/editorConstants"; import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; import { CONTAINER_GRID_PADDING, - MAIN_CONTAINER_WIDGET_ID, WIDGET_PADDING, GridDefaults, + MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { isEqual } from "lodash"; import { SelectedArenaDimensions } from "pages/common/CanvasSelectionArena"; @@ -47,10 +47,22 @@ function* selectAllWidgetsInAreaSaga( isMultiSelect: boolean; } = action.payload; - const padding = CONTAINER_GRID_PADDING + WIDGET_PADDING; + let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; + if ( + mainContainer.widgetId === MAIN_CONTAINER_WIDGET_ID || + mainContainer.type === "CONTAINER_WIDGET" + ) { + //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. + padding = CONTAINER_GRID_PADDING * 2; + } + if (mainContainer.noPad) { + // Widgets like ListWidget choose to have no container padding so will only have widget padding + padding = WIDGET_PADDING * 2; + } + // const padding = CONTAINER_GRID_PADDING + WIDGET_PADDING; const snapSpace = { snapColumnWidth: - (mainContainer.rightColumn - 2 * padding) / mainContainer.snapColumns, + (mainContainer.rightColumn - padding) / GridDefaults.DEFAULT_GRID_COLUMNS, snapColumnHeight: GridDefaults.DEFAULT_GRID_ROW_HEIGHT, }; // we use snapToNextRow, snapToNextColumn to determine if the selection rectangle is inverted @@ -70,7 +82,7 @@ function* selectAllWidgetsInAreaSaga( ); if (widgetOccupiedSpaces) { - const mainContainerWidgets = widgetOccupiedSpaces[MAIN_CONTAINER_WIDGET_ID]; + const mainContainerWidgets = widgetOccupiedSpaces[mainContainer.widgetId]; const widgets = Object.values(mainContainerWidgets || {}); const widgetsToBeSelected = widgets.filter((eachWidget) => { const { bottom, left, right, top } = eachWidget; @@ -104,11 +116,13 @@ function* selectAllWidgetsInAreaSaga( } } -function* startCanvasSelectionSaga() { +function* startCanvasSelectionSaga( + actionPayload: ReduxAction<{ widgetId: string }>, +) { const lastSelectedWidgets: string[] = yield select(getSelectedWidgets); const mainContainer: WidgetProps = yield select( getWidget, - MAIN_CONTAINER_WIDGET_ID, + actionPayload.payload.widgetId, ); const widgetOccupiedSpaces: | { From 134dd370652353716c4893502bf27f6b8fa51c3a Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 5 Jul 2021 14:49:41 +0530 Subject: [PATCH 06/68] dip --- .../components/editorComponents/DropTargetComponent.tsx | 9 +++++---- app/client/src/pages/common/CanvasSelectionArena.tsx | 9 +-------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 9a8ab1c9a27e..1d4e1fdf02c8 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -6,6 +6,7 @@ import React, { createContext, memo, useRef, + useEffect, } from "react"; import styled from "styled-components"; import { useDrop, XYCoord, DropTargetMonitor } from "react-dnd"; @@ -107,10 +108,10 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const { deselectAll, focusWidget, selectWidget } = useWidgetSelection(); const updateCanvasSnapRows = useCanvasSnapRowsUpdateHook(); - // useEffect(() => { - // const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); - // setRows(snapRows); - // }, [props.bottomRow, props.canExtend]); + useEffect(() => { + const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); + setRows(snapRows); + }, [props.bottomRow, props.canExtend]); const persistDropTargetRows = (widgetId: string, widgetBottomRow: number) => { const newRows = calculateDropTargetRows( diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 83e6aa1d9706..0dcd4196126d 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -239,14 +239,7 @@ export const CanvasSelectionArena = memo( selectionCanvas.removeEventListener("click", onClick); }; } - }, [ - appLayout, - currentPageId, - mainContainer.rightColumn, - mainContainer.bottomRow, - mainContainer.minHeight, - isDragging, - ]); + }, [appLayout, currentPageId, mainContainer, isDragging]); return appMode === APP_MODE.EDIT && !isDragging ? ( Date: Mon, 5 Jul 2021 21:52:02 +0530 Subject: [PATCH 07/68] dip --- app/client/src/pages/common/CanvasDraggingArena.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index b250686fdd42..822c07128440 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -156,7 +156,10 @@ export function CanvasDraggingArena({ isNotColliding: true, }, ]; - const { setDraggingNewWidget, setIsDragging } = useWidgetDragResize(); + const { + setDraggingNewWidget, + setDragItemsInitialParent, + } = useWidgetDragResize(); const canvasRef = React.useRef(null); const { persistDropTargetRows, updateDropTargetRows } = useContext( @@ -433,7 +436,7 @@ export function CanvasDraggingArena({ if (newWidget) { setDraggingNewWidget(false, undefined); } else { - setIsDragging(false); + setDragItemsInitialParent(false, "", ""); } onMouseOut(); if (isDragging && canvasIsDragging) { From 504fb1e945eacd06f0e181aad3c60a3b7d696cf9 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 7 Jul 2021 12:16:58 +0530 Subject: [PATCH 08/68] dip --- .../appsmith/PositionedContainer.tsx | 44 ++++------------ app/client/src/constants/WidgetConstants.tsx | 7 +++ .../hooks/usePositionedContainerZIndex.ts | 50 +++++++++++++++++++ 3 files changed, 68 insertions(+), 33 deletions(-) create mode 100644 app/client/src/utils/hooks/usePositionedContainerZIndex.ts diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index 91576bf1b1df..0bf8bd756426 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -1,28 +1,22 @@ import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; import { BaseStyle } from "widgets/BaseWidget"; -import { WIDGET_PADDING } from "constants/WidgetConstants"; +import { WidgetType, WIDGET_PADDING } from "constants/WidgetConstants"; import { generateClassName } from "utils/generators"; import styled from "styled-components"; import { useClickOpenPropPane } from "utils/hooks/useClickOpenPropPane"; import { stopEventPropagation } from "utils/AppsmithUtils"; -import { Layers } from "constants/Layers"; -import { useSelector } from "react-redux"; -import { AppState } from "reducers"; -import { getSelectedWidgets } from "selectors/ui"; +import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; -const PositionedWidget = styled.div<{ isDragging: boolean }>` +const PositionedWidget = styled.div<{ zIndexOnHover: number }>` &:hover { - z-index: ${(props) => - props.isDragging - ? Layers.positionedWidget - : Layers.positionedWidget + 1} !important; + z-index: ${(props) => props.zIndexOnHover} !important; } `; -type PositionedContainerProps = { +export type PositionedContainerProps = { style: BaseStyle; children: ReactNode; widgetId: string; - widgetType: string; + widgetType: WidgetType; selected?: boolean; focused?: boolean; resizeDisabled?: boolean; @@ -44,12 +38,8 @@ export function PositionedContainer(props: PositionedContainerProps) { .toLowerCase()}` ); }, [props.widgetType, props.widgetId]); - const isDragging = useSelector( - (state: AppState) => state.ui.widgetDragResize.isDragging, - ); - const selectedWidgets = useSelector(getSelectedWidgets); - const isThisWidgetDragging = - isDragging && selectedWidgets.includes(props.widgetId); + const { onHoverZIndex, zIndex } = usePositionedContainerZIndex(props); + const containerStyle: CSSProperties = useMemo(() => { return { position: "absolute", @@ -58,22 +48,10 @@ export function PositionedContainer(props: PositionedContainerProps) { height: props.style.componentHeight + (props.style.heightUnit || "px"), width: props.style.componentWidth + (props.style.widthUnit || "px"), padding: padding + "px", - zIndex: - isDragging && - !isThisWidgetDragging && - [ - "CONTAINER_WIDGET", - "FORM_WIDGET", - "LIST_WIDGET", - "TABS_WIDGET", - ].includes(props.widgetType) - ? 3 - : props.selected || props.focused - ? Layers.selectedWidget - : Layers.positionedWidget, + zIndex, backgroundColor: "inherit", }; - }, [props.style, isDragging]); + }, [props.style, onHoverZIndex, zIndex]); const openPropPane = useCallback((e) => openPropertyPane(e, props.widgetId), [ props.widgetId, @@ -86,12 +64,12 @@ export function PositionedContainer(props: PositionedContainerProps) { data-testid="test-widget" id={props.widgetId} key={`positioned-container-${props.widgetId}`} - isDragging={isDragging} onClick={stopEventPropagation} // Positioned Widget is the top enclosure for all widgets and clicks on/inside the widget should not be propogated/bubbled out of this Container. onClickCapture={openPropPane} //Before you remove: This is used by property pane to reference the element style={containerStyle} + zIndexOnHover={onHoverZIndex} > {props.children} diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index 83d0043686fd..f6d221463417 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -111,6 +111,13 @@ export const GridDefaults = { CANVAS_EXTENSION_OFFSET: 2, }; +export const DroppableWidgets: WidgetType[] = [ + WidgetTypes.CONTAINER_WIDGET, + WidgetTypes.FORM_WIDGET, + WidgetTypes.LIST_WIDGET, + WidgetTypes.TABS_WIDGET, +]; + // Note: Widget Padding + Container Padding === DEFAULT_GRID_ROW_HEIGHT to gracefully lose one row when a container is used, // which wud allow the user to place elements centered inside a container(columns are rendered proportionaly so it take cares of itselves). diff --git a/app/client/src/utils/hooks/usePositionedContainerZIndex.ts b/app/client/src/utils/hooks/usePositionedContainerZIndex.ts new file mode 100644 index 000000000000..2cc7cec1d27d --- /dev/null +++ b/app/client/src/utils/hooks/usePositionedContainerZIndex.ts @@ -0,0 +1,50 @@ +import { PositionedContainerProps } from "components/designSystems/appsmith/PositionedContainer"; +import { Layers } from "constants/Layers"; +import { DroppableWidgets } from "constants/WidgetConstants"; +import { useMemo } from "react"; +import { AppState } from "reducers"; +import { getSelectedWidgets } from "selectors/ui"; +import { useSelector } from "store"; + +export const usePositionedContainerZIndex = ( + props: PositionedContainerProps, +) => { + const droppableWidget = DroppableWidgets.includes(props.widgetType); + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); + const selectedWidgets = useSelector(getSelectedWidgets); + const isThisWidgetDragging = + isDragging && selectedWidgets.includes(props.widgetId); + + const zIndex = useMemo(() => { + if (isDragging) { + // dragging mode use cases + if (!isThisWidgetDragging && droppableWidget) { + return Layers.positionedWidget + 1; + } else { + // all non container widgets should go last into the background to not interfere with mouse move + // since it is not technically dragged but just drawn on canvas as the mouse moves. + return -1; + } + } else { + // common use case when nothing is dragged + return props.selected || props.focused + ? Layers.selectedWidget + : Layers.positionedWidget; + } + }, [ + isDragging, + isThisWidgetDragging, + droppableWidget, + props.selected, + props.focused, + ]); + + const zIndicesObj = useMemo(() => { + const onHoverZIndex = isDragging ? zIndex : Layers.positionedWidget + 1; + return { zIndex, onHoverZIndex }; + }, [isDragging, zIndex]); + + return zIndicesObj; +}; From 63a28d912b37ad17e6b944d624b53e48164d9085 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 7 Jul 2021 12:23:01 +0530 Subject: [PATCH 09/68] dip --- .../editorComponents/DragLayerComponent.tsx | 104 +----------------- .../editorComponents/DropTargetComponent.tsx | 32 ++---- 2 files changed, 8 insertions(+), 128 deletions(-) diff --git a/app/client/src/components/editorComponents/DragLayerComponent.tsx b/app/client/src/components/editorComponents/DragLayerComponent.tsx index 4bae37f88707..fcfccaca823e 100644 --- a/app/client/src/components/editorComponents/DragLayerComponent.tsx +++ b/app/client/src/components/editorComponents/DragLayerComponent.tsx @@ -1,8 +1,5 @@ -import React, { RefObject, useRef } from "react"; +import React from "react"; import styled from "styled-components"; -import { useDragLayer, XYCoord } from "react-dnd"; -import { noCollision } from "utils/WidgetPropsUtils"; -import { OccupiedSpace } from "constants/editorConstants"; import { CONTAINER_GRID_PADDING, GridDefaults, @@ -12,7 +9,6 @@ const WrappedDragLayer = styled.div<{ columnWidth: number; rowHeight: number; noPad: boolean; - ref: RefObject; }>` position: absolute; pointer-events: none; @@ -42,113 +38,15 @@ const WrappedDragLayer = styled.div<{ type DragLayerProps = { parentRowHeight: number; - canDropTargetExtend: boolean; parentColumnWidth: number; - visible: boolean; - occupiedSpaces?: OccupiedSpace[]; - onBoundsUpdate: (rect: DOMRect) => void; - isOver: boolean; - parentRows?: number; - parentCols?: number; - isResizing?: boolean; - parentWidgetId: string; - force: boolean; noPad: boolean; }; function DragLayerComponent(props: DragLayerProps) { - // const { updateDropTargetRows } = useContext(DropTargetContext); - const dropTargetMask: RefObject = React.useRef(null); - // useEffect(() => { - // const el = dropZoneRef.current; - // const scrollParent: Element | null = getNearestParentCanvas( - // dropTargetMask.current, - // ); - // if (dropTargetMask.current) { - // if (el && props.canDropTargetExtend) { - // scrollElementIntoParentCanvasView(el, scrollParent); - // } - // } - // }); - - const dropTargetOffset = useRef({ - x: 0, - y: 0, - }); - const { isDragging } = useDragLayer((monitor) => ({ - isDragging: monitor.isDragging(), - currentOffset: monitor.getSourceClientOffset(), - widget: monitor.getItem(), - canDrop: noCollision( - monitor.getSourceClientOffset() as XYCoord, - props.parentColumnWidth, - props.parentRowHeight, - monitor.getItem(), - dropTargetOffset.current, - props.occupiedSpaces, - props.parentRows, - props.parentCols, - ), - })); - - // if ( - // currentOffset && - // props.isOver && - // props.canDropTargetExtend && - // isDragging - // ) { - // const row = currentDropRow( - // props.parentRowHeight, - // dropTargetOffset.current.y, - // currentOffset.y, - // widget, - // ); - - // updateDropTargetRows && updateDropTargetRows(widget.widgetId, row); - // } - - // let widgetWidth = 0; - // let widgetHeight = 0; - // if (widget) { - // widgetWidth = widget.columns - // ? widget.columns - // : widget.rightColumn - widget.leftColumn; - // widgetHeight = widget.rows ? widget.rows : widget.bottomRow - widget.topRow; - // } - // useEffect(() => { - // const el = dropTargetMask.current; - // if (el) { - // const rect = el.getBoundingClientRect(); - // if ( - // rect.x !== dropTargetOffset.current.x || - // rect.y !== dropTargetOffset.current.y - // ) { - // dropTargetOffset.current = { x: rect.x, y: rect.y }; - // props.onBoundsUpdate && props.onBoundsUpdate(rect); - // } - // } - // }); - - if ( - (!isDragging || !props.visible || !props.isOver) && - !props.force && - !props.isResizing - ) { - return null; - } - - /* - When the parent offsets are not updated, we don't need to show the dropzone, as the dropzone - will be rendered at an incorrect coordinates. - We can be sure that the parent offset has been calculated - when the coordiantes are not [0,0]. - */ - return ( ); diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 1d4e1fdf02c8..648ed37ca3be 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -79,9 +79,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const { updateWidget } = useContext(EditorContext); const occupiedSpaces = useSelector(getOccupiedSpaces); - const selectedWidget = useSelector( - (state: AppState) => state.ui.widgetDragResize.lastSelectedWidget, - ); const isResizing = useSelector( (state: AppState) => state.ui.widgetDragResize.isResizing, ); @@ -150,13 +147,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { return false; }; - const isChildFocused = - !!childWidgets && - !!selectedWidget && - childWidgets.length > 0 && - childWidgets.indexOf(selectedWidget) > -1; - - const isChildResizing = !!isResizing && isChildFocused; // Make this component a drop target const [{ isExactlyOver }] = useDrop({ accept: Object.values(WidgetTypes), @@ -288,21 +278,13 @@ export function DropTargetComponent(props: DropTargetComponentProps) { {!(childWidgets && childWidgets.length) && !isDragging && !props.parentId && } - + {(isDragging || isResizing) && ( + + )} ); From 09819eadd5c378a7220171d02fc6e32ab145fd31 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 7 Jul 2021 16:02:28 +0530 Subject: [PATCH 10/68] dip --- .../editorComponents/DraggableComponent.tsx | 98 +++++-------------- .../src/pages/common/CanvasDraggingArena.tsx | 11 +-- .../reducers/uiReducers/dragResizeReducer.ts | 30 +++--- .../src/utils/hooks/dragResizeHooks.tsx | 32 +++--- 4 files changed, 49 insertions(+), 122 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 5bd997763483..ab9f644153a8 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -1,17 +1,11 @@ import React, { CSSProperties, useState } from "react"; import styled from "styled-components"; import { WidgetProps } from "widgets/BaseWidget"; -import { useDrag, DragSourceMonitor } from "react-dnd"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import { useDispatch, useSelector } from "react-redux"; import { AppState } from "reducers"; import { getColorWithOpacity } from "constants/DefaultTheme"; -import { - useShowPropertyPane, - useShowTableFilterPane, - useWidgetDragResize, -} from "utils/hooks/dragResizeHooks"; -import AnalyticsUtil from "utils/AnalyticsUtil"; +import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; import { commentModeSelector } from "selectors/commentsSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { selectWidgetInitAction } from "actions/widgetSelectionActions"; @@ -44,41 +38,34 @@ type DraggableComponentProps = WidgetProps; /** * can drag helper function for react-dnd hook * - * @param isResizing + * @param isResizingOrDragging * @param isDraggingDisabled * @param props * @returns */ export const canDrag = ( - isResizing: boolean, + isResizingOrDragging: boolean, isDraggingDisabled: boolean, props: any, isCommentMode: boolean, ) => { return ( - !isResizing && !isDraggingDisabled && !props?.dragDisabled && !isCommentMode + !isResizingOrDragging && + !isDraggingDisabled && + !props?.dragDisabled && + !isCommentMode ); }; function DraggableComponent(props: DraggableComponentProps) { - // Dispatch hook handy to toggle property pane - const showPropertyPane = useShowPropertyPane(); - const showTableFilterPane = useShowTableFilterPane(); - // Dispatch hook handy to set a widget as focused/selected - const { focusWidget, selectWidget } = useWidgetSelection(); + const { focusWidget } = useWidgetSelection(); const isCommentMode = useSelector(commentModeSelector); // Dispatch hook handy to set any `DraggableComponent` as dragging/ not dragging // The value is boolean - const { setDragItemsInitialParent, setIsDragging } = useWidgetDragResize(); - - // This state tells us which widget is selected - // The value is the widgetId of the selected widget - const selectedWidget = useSelector( - (state: AppState) => state.ui.widgetDragResize.lastSelectedWidget, - ); + const { setDraggingState } = useWidgetDragResize(); const selectedWidgets = useSelector( (state: AppState) => state.ui.widgetDragResize.selectedWidgets, @@ -106,54 +93,11 @@ function DraggableComponent(props: DraggableComponentProps) { (state: AppState) => state.ui.widgetDragResize.isDraggingDisabled, ); - const [{ isCurrentWidgetDragging }] = useDrag({ - item: props as WidgetProps, - collect: (monitor: DragSourceMonitor) => ({ - isCurrentWidgetDragging: monitor.isDragging(), - }), - begin: () => { - // When this draggable starts dragging - - // Make sure that this widget is selected - selectWidget && - selectedWidget !== props.widgetId && - selectWidget(props.widgetId); - // Make sure that this tableFilterPane should close - showTableFilterPane && showTableFilterPane(); - // Tell the rest of the application that a widget has started dragging - setIsDragging && setIsDragging(true); - AnalyticsUtil.logEvent("WIDGET_DRAG", { - widgetName: props.widgetName, - widgetType: props.type, - }); - }, - end: (widget, monitor) => { - // When this draggable is dropped, we try to open the propertypane - // We pass the second parameter to make sure the previous toggle state (open/close) - // of the property pane is taken into account. - // See utils/hooks/dragResizeHooks.tsx - const didDrop = monitor.didDrop(); - if (didDrop) { - showPropertyPane && showPropertyPane(props.widgetId, undefined, true); - } - // Take this to the bottom of the stack. So that it runs last. - // We do this because, we don't want erroraneous mouse clicks to propagate. - setTimeout(() => setIsDragging && setIsDragging(false), 0); - AnalyticsUtil.logEvent("WIDGET_DROP", { - widgetName: props.widgetName, - widgetType: props.type, - didDrop: didDrop, - }); - }, - canDrag: () => { - // Dont' allow drag if we're resizing or the drag of `DraggableComponent` is disabled - return canDrag(isResizing, isDraggingDisabled, props, isCommentMode); - }, - }); - // True when any widget is dragging or resizing, including this one const isResizingOrDragging = !!isResizing || !!isDragging; - + const isCurrentWidgetDragging = + isDragging && selectedWidgets.includes(props.widgetId); + const isCurrentWidgetDraggingResizing = isResizing && isCurrentWidgetDragging; // When mouse is over this draggable const handleMouseOver = (e: any) => { focusWidget && @@ -174,10 +118,7 @@ function DraggableComponent(props: DraggableComponentProps) { const widgetBoundaries = ( { - if (mightBeDragging && !isResizingOrDragging) { + if (mightBeDragging && allowDrag) { e.preventDefault(); - setDragItemsInitialParent(true, props.parentId || "", props.widgetId); + setDraggingState(true, props.parentId || "", props.widgetId); if (!selectedWidgets.includes(props.widgetId)) { dispatch(selectWidgetInitAction(props.widgetId)); } @@ -210,7 +156,7 @@ function DraggableComponent(props: DraggableComponentProps) { { - if (!e.metaKey && !isResizingOrDragging) { + if (!e.metaKey && allowDrag) { setMightBeDragging(true); e.stopPropagation(); } diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 822c07128440..ed191cdea5b7 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -107,7 +107,7 @@ export function CanvasDraggingArena({ widgetId: string; }) { const dragParent = useSelector( - (state: AppState) => state.ui.widgetDragResize.dragParent, + (state: AppState) => state.ui.widgetDragResize.dragGroupActualParent, ); const isResizing = useSelector( (state: AppState) => state.ui.widgetDragResize.isResizing, @@ -125,7 +125,7 @@ export function CanvasDraggingArena({ const allWidgets = useSelector(getWidgets); // const widget = useSelector((state: AppState) => getWidget(state, widgetId)); const dragCenter = useSelector( - (state: AppState) => state.ui.widgetDragResize.dragCenter, + (state: AppState) => state.ui.widgetDragResize.draggingGroupCenter, ); const dragCenterSpace = childrenOccupiedSpaces.find( (each) => each.id === dragCenter, @@ -156,10 +156,7 @@ export function CanvasDraggingArena({ isNotColliding: true, }, ]; - const { - setDraggingNewWidget, - setDragItemsInitialParent, - } = useWidgetDragResize(); + const { setDraggingNewWidget, setDraggingState } = useWidgetDragResize(); const canvasRef = React.useRef(null); const { persistDropTargetRows, updateDropTargetRows } = useContext( @@ -436,7 +433,7 @@ export function CanvasDraggingArena({ if (newWidget) { setDraggingNewWidget(false, undefined); } else { - setDragItemsInitialParent(false, "", ""); + setDraggingState(false); } onMouseOut(); if (isDragging && canvasIsDragging) { diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index cf32b9a38d7d..6284605da043 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -6,8 +6,8 @@ const initialState: WidgetDragResizeState = { isDraggingDisabled: false, isDragging: false, isResizing: false, - dragParent: "", - dragCenter: "", + dragGroupActualParent: "", + draggingGroupCenter: "", newWidget: undefined, lastSelectedWidget: undefined, selectedWidgets: [], @@ -22,33 +22,27 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { ) => { state.isDraggingDisabled = action.payload.isDraggingDisabled; }, - [ReduxActionTypes.SET_NEW_WIDGET_DRAGGING]: ( + [ReduxActionTypes.SET_WIDGET_DRAGGING]: ( state: WidgetDragResizeState, action: ReduxAction<{ isDragging: boolean; - dragParent: string; - dragCenter: string; + dragGroupActualParent: string; + draggingGroupCenter: string; }>, ) => { - state.dragParent = action.payload.dragParent; + state.dragGroupActualParent = action.payload.dragGroupActualParent; state.isDragging = action.payload.isDragging; - state.dragCenter = action.payload.dragCenter; + state.draggingGroupCenter = action.payload.draggingGroupCenter; }, - [ReduxActionTypes.SET_NEW_WIDGET_DRAGGING_2]: ( + [ReduxActionTypes.SET_NEW_WIDGET_DRAGGING]: ( state: WidgetDragResizeState, action: ReduxAction<{ isDragging: boolean; - widget: string; + newWidgetProps: any; }>, ) => { state.isDragging = action.payload.isDragging; - state.newWidget = action.payload.widget; - }, - [ReduxActionTypes.SET_WIDGET_DRAGGING]: ( - state: WidgetDragResizeState, - action: ReduxAction<{ isDragging: boolean }>, - ) => { - state.isDragging = action.payload.isDragging; + state.newWidget = action.payload.newWidgetProps; }, [ReduxActionTypes.SET_WIDGET_RESIZING]: ( state: WidgetDragResizeState, @@ -134,8 +128,8 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { export type WidgetDragResizeState = { isDraggingDisabled: boolean; isDragging: boolean; - dragParent: string; - dragCenter: string; + dragGroupActualParent: string; + draggingGroupCenter: string; isResizing: boolean; lastSelectedWidget?: string; focusedWidget?: string; diff --git a/app/client/src/utils/hooks/dragResizeHooks.tsx b/app/client/src/utils/hooks/dragResizeHooks.tsx index 25d87ce6ed99..fdb9192b5e8c 100644 --- a/app/client/src/utils/hooks/dragResizeHooks.tsx +++ b/app/client/src/utils/hooks/dragResizeHooks.tsx @@ -95,44 +95,34 @@ export const useCanvasSnapRowsUpdateHook = () => { export const useWidgetDragResize = () => { const dispatch = useDispatch(); return { - setIsDragging: useCallback( - (isDragging: boolean) => { - if (isDragging) { - document.body.classList.add("dragging"); - } else { - document.body.classList.remove("dragging"); - } - dispatch({ - type: ReduxActionTypes.SET_WIDGET_DRAGGING, - payload: { isDragging }, - }); - }, - [dispatch], - ), setDraggingNewWidget: useCallback( - (isDragging: boolean, widget: any) => { + (isDragging: boolean, newWidgetProps: any) => { if (isDragging) { document.body.classList.add("dragging"); } else { document.body.classList.remove("dragging"); } dispatch({ - type: ReduxActionTypes.SET_NEW_WIDGET_DRAGGING_2, - payload: { isDragging, widget }, + type: ReduxActionTypes.SET_NEW_WIDGET_DRAGGING, + payload: { isDragging, newWidgetProps }, }); }, [dispatch], ), - setDragItemsInitialParent: useCallback( - (isDragging: boolean, dragParent: string, dragCenter: string) => { + setDraggingState: useCallback( + ( + isDragging: boolean, + dragGroupActualParent = "", + draggingGroupCenter = "", + ) => { if (isDragging) { document.body.classList.add("dragging"); } else { document.body.classList.remove("dragging"); } dispatch({ - type: ReduxActionTypes.SET_NEW_WIDGET_DRAGGING, - payload: { isDragging, dragParent, dragCenter }, + type: ReduxActionTypes.SET_WIDGET_DRAGGING, + payload: { isDragging, dragGroupActualParent, draggingGroupCenter }, }); }, [dispatch], From 0dbba5ca11c6591a4d605488777c612be336c84e Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 7 Jul 2021 16:14:42 +0530 Subject: [PATCH 11/68] dip --- .../editorComponents/DraggableComponent.tsx | 2 +- .../editorComponents/DropTargetComponent.tsx | 112 +----------------- 2 files changed, 4 insertions(+), 110 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index ab9f644153a8..6885a57f7b31 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -131,7 +131,7 @@ function DraggableComponent(props: DraggableComponentProps) { .split("_") .join("") .toLowerCase()}`; - // const { setIsDragging } = useWidgetDragResize(); + const allowDrag = canDrag( isResizingOrDragging, isDraggingDisabled, diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 648ed37ca3be..19cd55e6dbbf 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -1,6 +1,5 @@ import React, { useState, - useContext, ReactNode, Context, createContext, @@ -9,19 +8,11 @@ import React, { useEffect, } from "react"; import styled from "styled-components"; -import { useDrop, XYCoord, DropTargetMonitor } from "react-dnd"; import { WidgetProps } from "widgets/BaseWidget"; -import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer"; -import { - widgetOperationParams, - noCollision, - getCanvasSnapRows, -} from "utils/WidgetPropsUtils"; -import { EditorContext } from "components/editorComponents/EditorContextProvider"; +import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; import { MAIN_CONTAINER_WIDGET_ID, GridDefaults, - WidgetTypes, } from "constants/WidgetConstants"; import { calculateDropTargetRows } from "./DropTargetUtils"; import DragLayerComponent from "./DragLayerComponent"; @@ -77,7 +68,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); - const { updateWidget } = useContext(EditorContext); const occupiedSpaces = useSelector(getOccupiedSpaces); const isResizing = useSelector( (state: AppState) => state.ui.widgetDragResize.isResizing, @@ -86,11 +76,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { (state: AppState) => state.ui.widgetDragResize.isDragging, ); - const spacesOccupiedBySiblingWidgets = - occupiedSpaces && occupiedSpaces[props.widgetId] - ? occupiedSpaces[props.widgetId] - : undefined; - const childWidgets = useSelector( (state: AppState) => state.entities.canvasWidgets[props.widgetId].children, ); @@ -98,11 +83,10 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const occupiedSpacesByChildren = occupiedSpaces && occupiedSpaces[props.widgetId]; - const [dropTargetOffset, setDropTargetOffset] = useState({ x: 0, y: 0 }); const [rows, setRows] = useState(snapRows); const showPropertyPane = useShowPropertyPane(); - const { deselectAll, focusWidget, selectWidget } = useWidgetSelection(); + const { deselectAll, focusWidget } = useWidgetSelection(); const updateCanvasSnapRows = useCanvasSnapRowsUpdateHook(); useEffect(() => { @@ -136,7 +120,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1, occupiedSpacesByChildren, ); - console.log(rows, newRows); if (rows < newRows) { setRows(newRows + 1); @@ -147,91 +130,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { return false; }; - // Make this component a drop target - const [{ isExactlyOver }] = useDrop({ - accept: Object.values(WidgetTypes), - options: { - arePropsEqual: () => { - return true; - }, - }, - drop(widget: WidgetProps & Partial, monitor) { - // Make sure we're dropping in this container. - if (isExactlyOver) { - const updateWidgetParams = widgetOperationParams( - widget, - monitor.getSourceClientOffset() as XYCoord, - dropTargetOffset, - props.snapColumnSpace, - props.snapRowSpace, - widget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : props.widgetId, - ); - - // const widgetBottomRow = getWidgetBottomRow(widget, updateWidgetParams); - const widgetBottomRow = - updateWidgetParams.payload.topRow + - (updateWidgetParams.payload.rows || widget.bottomRow - widget.topRow); - - // Only show propertypane if this is a new widget. - // If it is not a new widget, then let the DraggableComponent handle it. - // Give evaluations a second to complete. - setTimeout(() => { - if (showPropertyPane && updateWidgetParams.payload.newWidgetId) { - showPropertyPane(updateWidgetParams.payload.newWidgetId); - // toggleEditWidgetName(updateWidgetParams.payload.newWidgetId, true); - } - }, 100); - - // Select the widget if it is a new widget - selectWidget && selectWidget(widget.widgetId); - persistDropTargetRows(widget.widgetId, widgetBottomRow); - - /* Finally update the widget */ - updateWidget && - updateWidget( - updateWidgetParams.operation, - updateWidgetParams.widgetId, - updateWidgetParams.payload, - ); - } - return undefined; - }, - // Collect isOver for ui transforms when hovering over this component - collect: (monitor: DropTargetMonitor) => ({ - isExactlyOver: monitor.isOver({ shallow: true }), - isOver: monitor.isOver(), - }), - // Only allow drop if the drag object is directly over this component - // As opposed to the drag object being over a child component, or outside the component bounds - // Also only if the dropzone does not overlap any existing children - canDrop: (widget, monitor) => { - // Check if the draggable is the same as the dropTarget - if (isExactlyOver) { - const hasCollision = !noCollision( - monitor.getSourceClientOffset() as XYCoord, - props.snapColumnSpace, - props.snapRowSpace, - widget, - dropTargetOffset, - spacesOccupiedBySiblingWidgets, - rows, - GridDefaults.DEFAULT_GRID_COLUMNS, - ); - return !hasCollision; - } - return false; - }, - }); - - const handleBoundsUpdate = (rect: DOMRect) => { - if (rect.x !== dropTargetOffset.x || rect.y !== dropTargetOffset.y) { - setDropTargetOffset({ - x: rect.x, - y: rect.y, - }); - } - }; - const handleFocus = (e: any) => { if (!isResizing && !isDragging) { if (!props.parentId) { @@ -249,9 +147,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { : "100%"; const boxShadow = - (isResizing || isDragging) && - isExactlyOver && - props.widgetId === MAIN_CONTAINER_WIDGET_ID + (isResizing || isDragging) && props.widgetId === MAIN_CONTAINER_WIDGET_ID ? "0px 0px 0px 1px #DDDDDD" : "0px 0px 0px 1px transparent"; const dropRef = useRef(null); @@ -261,8 +157,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { value={{ updateDropTargetRows, persistDropTargetRows, - handleBoundsUpdate, - dropRef: !props.dropDisabled ? dropRef : undefined, }} > Date: Wed, 7 Jul 2021 16:31:45 +0530 Subject: [PATCH 12/68] dip --- .../editorComponents/DragLayerComponent.tsx | 3 +- .../editorComponents/DropTargetComponent.tsx | 7 +--- .../src/pages/common/CanvasDraggingArena.tsx | 35 +------------------ 3 files changed, 3 insertions(+), 42 deletions(-) diff --git a/app/client/src/components/editorComponents/DragLayerComponent.tsx b/app/client/src/components/editorComponents/DragLayerComponent.tsx index fcfccaca823e..90af4ab3224e 100644 --- a/app/client/src/components/editorComponents/DragLayerComponent.tsx +++ b/app/client/src/components/editorComponents/DragLayerComponent.tsx @@ -32,8 +32,7 @@ const WrappedDragLayer = styled.div<{ ); background-size: ${(props) => props.columnWidth - GRID_POINT_SIZE / GridDefaults.DEFAULT_GRID_COLUMNS}px - ${(props) => - props.rowHeight - GRID_POINT_SIZE / GridDefaults.DEFAULT_GRID_COLUMNS}px; + ${(props) => props.rowHeight}px; `; type DragLayerProps = { diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 19cd55e6dbbf..6e799a534116 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -4,7 +4,6 @@ import React, { Context, createContext, memo, - useRef, useEffect, } from "react"; import styled from "styled-components"; @@ -59,8 +58,6 @@ export const DropTargetContext: Context<{ widgetBottomRow: number, ) => number | false; persistDropTargetRows?: (widgetId: string, row: number) => void; - handleBoundsUpdate?: (rect: DOMRect) => void; - dropRef?: React.RefObject; }> = createContext({}); export function DropTargetComponent(props: DropTargetComponentProps) { @@ -122,7 +119,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { ); if (rows < newRows) { - setRows(newRows + 1); + setRows(newRows); return newRows; } return false; @@ -150,7 +147,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { (isResizing || isDragging) && props.widgetId === MAIN_CONTAINER_WIDGET_ID ? "0px 0px 0px 1px #DDDDDD" : "0px 0px 0px 1px transparent"; - const dropRef = useRef(null); return ( rows) { + if (top > rows - GridDefaults.CANVAS_EXTENSION_OFFSET) { return updateDropTargetRows && updateDropTargetRows(widgetId, top); } }; From 675317b846fecf5b677911141d4bfcb66b7344d1 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 7 Jul 2021 20:11:39 +0530 Subject: [PATCH 13/68] dip --- .../editorComponents/DraggableComponent.tsx | 77 ++++++--- .../editorComponents/ResizableComponent.tsx | 14 +- .../src/constants/ReduxActionConstants.tsx | 1 - .../src/pages/common/CanvasDraggingArena.tsx | 152 +++--------------- .../reducers/uiReducers/dragResizeReducer.ts | 4 + app/client/src/utils/WidgetPropsUtils.tsx | 17 +- app/client/src/utils/helpers.tsx | 49 +----- .../src/utils/hooks/dragResizeHooks.tsx | 8 +- app/client/src/widgets/ContainerWidget.tsx | 4 - 9 files changed, 106 insertions(+), 220 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 6885a57f7b31..6a3879ba7765 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -1,4 +1,4 @@ -import React, { CSSProperties, useState } from "react"; +import React, { CSSProperties, useRef } from "react"; import styled from "styled-components"; import { WidgetProps } from "widgets/BaseWidget"; import { WIDGET_PADDING } from "constants/WidgetConstants"; @@ -9,6 +9,7 @@ import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; import { commentModeSelector } from "selectors/commentsSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { selectWidgetInitAction } from "actions/widgetSelectionActions"; +import { useCallback } from "react"; const DraggableWrapper = styled.div` display: block; @@ -97,7 +98,8 @@ function DraggableComponent(props: DraggableComponentProps) { const isResizingOrDragging = !!isResizing || !!isDragging; const isCurrentWidgetDragging = isDragging && selectedWidgets.includes(props.widgetId); - const isCurrentWidgetDraggingResizing = isResizing && isCurrentWidgetDragging; + const isCurrentWidgetResizing = + isResizing && selectedWidgets.includes(props.widgetId); // When mouse is over this draggable const handleMouseOver = (e: any) => { focusWidget && @@ -118,7 +120,7 @@ function DraggableComponent(props: DraggableComponentProps) { const widgetBoundaries = ( { - if (mightBeDragging && allowDrag) { - e.preventDefault(); - setDraggingState(true, props.parentId || "", props.widgetId); - if (!selectedWidgets.includes(props.widgetId)) { - dispatch(selectWidgetInitAction(props.widgetId)); + const mightBeDragging = useRef(false); + const draggableRef = useRef(null); + const startPoints = useRef({ + top: props.bottomRow / 2, + left: props.rightColumn / 2, + }); + const onMouseMove = useCallback( + (e: any) => { + if (draggableRef.current && mightBeDragging.current && allowDrag) { + e.preventDefault(); + if (!selectedWidgets.includes(props.widgetId)) { + dispatch(selectWidgetInitAction(props.widgetId)); + } + mightBeDragging.current = false; + setDraggingState( + true, + props.parentId || "", + props.widgetId, + startPoints.current, + ); + e.stopPropagation(); } - setMightBeDragging(false); - e.stopPropagation(); - } - }; + }, + [allowDrag, selectedWidgets, setDraggingState], + ); + + const onMouseDown = useCallback( + (e: any) => { + if (!e.metaKey && allowDrag && draggableRef.current) { + mightBeDragging.current = true; + const bounds = draggableRef.current.getBoundingClientRect(); + startPoints.current = { + top: (e.clientY - bounds.top) / props.parentRowSpace, + left: (e.clientX - bounds.left) / props.parentColumnSpace, + }; + e.stopPropagation(); + } + }, + [allowDrag], + ); + + const onMouseUp = useCallback(() => { + mightBeDragging.current = false; + }, []); + return ( { - if (!e.metaKey && allowDrag) { - setMightBeDragging(true); - e.stopPropagation(); - } - }} - onMouseMove={mouseMove} + onMouseDown={onMouseDown} + onMouseMove={onMouseMove} onMouseOver={handleMouseOver} - onMouseUp={() => { - setMightBeDragging(false); - }} + onMouseUp={onMouseUp} + ref={draggableRef} style={style} > {shouldRenderComponent && props.children} diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 6075a76aa848..e1cd68e9d4aa 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -137,8 +137,18 @@ export const ResizableComponent = memo(function ResizableComponent( if (updateDropTargetRows) { updated = !!updateDropTargetRows(props.widgetId, bottom); const el = resizableRef.current; - const scrollParent = getNearestParentCanvas(resizableRef.current); - scrollElementIntoParentCanvasView(el, scrollParent); + if (el) { + const { height } = el?.getBoundingClientRect(); + const scrollParent = getNearestParentCanvas(resizableRef.current); + scrollElementIntoParentCanvasView( + { + top: 40, + height, + }, + scrollParent, + el, + ); + } } const delta: UIElementSize = { diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index cc144053ceb5..e5660a2a1515 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -326,7 +326,6 @@ export const ReduxActionTypes: { [key: string]: string } = { FOCUS_WIDGET: "FOCUS_WIDGET", SET_WIDGET_DRAGGING: "SET_WIDGET_DRAGGING", SET_NEW_WIDGET_DRAGGING: "SET_NEW_WIDGET_DRAGGING", - SET_NEW_WIDGET_DRAGGING_2: "SET_NEW_WIDGET_DRAGGING_2", SET_WIDGET_RESIZING: "SET_WIDGET_RESIZING", ADD_TABLE_WIDGET_FROM_QUERY: "ADD_TABLE_WIDGET_FROM_QUERY", SEARCH_APPLICATIONS: "SEARCH_APPLICATIONS", diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 803149dd0fde..fe05f56fed08 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -14,13 +14,12 @@ import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; import { XYCoord } from "react-dnd"; import { getDropZoneOffsets, - isDropZoneOccupied, - isWidgetOverflowingParentBounds, + noCollision, widgetOperationParams, } from "utils/WidgetPropsUtils"; import { getSnappedXY } from "components/editorComponents/Dropzone"; import { getNearestParentCanvas } from "utils/generators"; -import { scrollElementIntoParentCanvasView2 } from "utils/helpers"; +import { scrollElementIntoParentCanvasView } from "utils/helpers"; import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; import { getWidgets } from "sagas/selectors"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; @@ -52,54 +51,13 @@ export interface SelectedArenaDimensions { height: number; } -const noCollision = ( - clientOffset: XYCoord, - colWidth: number, - rowHeight: number, - dropTargetOffset: XYCoord, - widgetWidth: number, - widgetHeight: number, - widgetId: string, - occupiedSpaces?: OccupiedSpace[], - rows?: number, - cols?: number, -): boolean => { - if (clientOffset && dropTargetOffset) { - // if (widget.detachFromLayout) { - // return true; - // } - const [left, top] = getDropZoneOffsets( - colWidth, - rowHeight, - clientOffset as XYCoord, - dropTargetOffset, - ); - if (left < 0 || top < 0) { - return false; - } - const currentOffset = { - left, - right: left + widgetWidth, - top, - bottom: top + widgetHeight, - }; - return ( - !isDropZoneOccupied(currentOffset, widgetId, occupiedSpaces) && - !isWidgetOverflowingParentBounds({ rows, cols }, currentOffset) - ); - } - return false; -}; - export function CanvasDraggingArena({ - // childWidgets, noPad, snapColumnSpace, snapRows, snapRowSpace, widgetId, }: { - // childWidgets: string[]; noPad?: boolean; snapColumnSpace: number; snapRows: number; @@ -119,6 +77,9 @@ export function CanvasDraggingArena({ const isDragging = useSelector( (state: AppState) => state.ui.widgetDragResize.isDragging, ); + const dragStartPoints = useSelector( + (state: AppState) => state.ui.widgetDragResize.startPoints, + ); const newWidget = useSelector( (state: AppState) => state.ui.widgetDragResize.newWidget, ); @@ -199,14 +160,13 @@ export function CanvasDraggingArena({ }; } - // const el = canvasRef?.current; const scrollParent: Element | null = getNearestParentCanvas( canvasRef.current, ); if (canvasRef.current) { // if (el && props.canDropTargetExtend) { if (groupBlock) { - scrollElementIntoParentCanvasView2( + scrollElementIntoParentCanvasView( groupBlock, scrollParent, canvasRef.current, @@ -350,45 +310,6 @@ export function CanvasDraggingArena({ let canvasCtx: any = canvasRef.current.getContext("2d"); canvasCtx.globalCompositeOperation = "destination-over"; canvasCtx.scale(scale, scale); - // const drawDragLayer = debounce( - // (rows) => { - // if (canvasRef.current && canvasDragLayerRef.current) { - // const { height, width } = canvasRef.current.getBoundingClientRect(); - // canvasDragLayerRef.current.width = width * scale; - // canvasDragLayerRef.current.height = height * scale; - // const canvasCtx: any = canvasDragLayerRef.current.getContext("2d"); - // canvasCtx.clearRect(0, 0, width, height); - // canvasCtx.beginPath(); // clear path if it has been used previously - // // modify method to add to path instead - // const draw = (x: any, y: any, width: any, height: any) => { - // canvasCtx.fillStyle = `${"rgb(0, 0, 0, 1)"}`; - // canvasCtx.strokeStyle = `${"rgb(0, 0, 0, 1)"}`; - // canvasCtx.rect(x, y, width, height); - // }; - // for ( - // let x = noPad ? 0 : CONTAINER_GRID_PADDING; - // x < width; - // x += snapColumnSpace - // ) { - // for ( - // let y = noPad ? 0 : CONTAINER_GRID_PADDING; - // y < rows * snapRowSpace + (widgetId === "0" ? 200 : 0); - // y += snapRowSpace - // ) { - // draw(x, y, 1, 1); - // } - // } - - // // when done, fill once - // canvasCtx.fill(); - // } - // }, - // 0, - // { - // leading: true, - // trailing: true, - // }, - // ); const startPoints = { left: 20, @@ -408,30 +329,29 @@ export function CanvasDraggingArena({ } }; - const onMouseDown = (e: any) => { + const onMouseDown = () => { if ( !isResizing && isDragging && !canvasIsDragging && canvasRef.current ) { - canvasIsDragging = true; - if ( - dragParent === widgetId && - startPoints.left === 20 && - startPoints.top === 20 - ) { - startPoints.left = e.offsetX; - startPoints.top = e.offsetY; + if (dragCenterSpace) { + startPoints.left = + ((dragParent === widgetId ? dragCenterSpace.left : 0) + + dragStartPoints.left) * + snapColumnSpace; + startPoints.top = + ((dragParent === widgetId ? dragCenterSpace.top : 0) + + dragStartPoints.top) * + snapRowSpace; } + canvasIsDragging = true; canvasRef.current.style.zIndex = "2"; - // drawDragLayer(rows); } }; const onMouseMove = (e: any) => { - if (canvasIsDragging && canvasRef.current) { - // console.log(startPoints, e.offsetX, draggingCanvas.offsetLeft); - + if (isDragging && canvasIsDragging && canvasRef.current) { const diff = { left: e.offsetX - startPoints.left - parentDiff.left, top: e.offsetY - startPoints.top - parentDiff.top, @@ -479,27 +399,16 @@ export function CanvasDraggingArena({ drawRectangle(each); }); } - // if (rowDiff) { - // newRectanglesToDraw = newRectanglesToDraw.map((each) => { - // return { - // ...each, - // top: each.top - (rows - snapRows) * snapRowSpace, - // }; - // }); - // console.log({ rowDiff, rectanglesToDraw, newRectanglesToDraw }); - // } scrollToKeepUp(newRectanglesToDraw); } else { - onMouseDown(e); + onMouseDown(); } }; - console.log("I am initiated again"); let notDoneYet = false; const drawInit = throttle( debounce( (rowDiff, diff) => { - console.count("drawInit"); notDoneYet = true; if (canvasRef.current) { newRectanglesToDraw = rectanglesToDraw.map((each) => { @@ -541,29 +450,6 @@ export function CanvasDraggingArena({ }, ); - // const drawInit = debounce( - // (rowDiff, diff, occSpaces) => { - // // // if (rowDiff) { - // // drawDragLayer(); - // // // } - // if (rowDiff) { - // // startPoints.top = startPoints.top + rowDiff * snapRowSpace; - // newRectanglesToDraw = rectanglesToDraw.map((each) => { - // return { - // ...each, - // top: each.top + rowDiff * snapRowSpace - // }; - // }); - // } - - // scrollToKeepUp(newRectanglesToDraw); - // }, - // 100, - // { - // leading: true, - // trailing: true, - // }, - // ); const drawRectangle = (selectionDimensions: { top: number; left: number; diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index 6284605da043..062396633288 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -13,6 +13,7 @@ const initialState: WidgetDragResizeState = { selectedWidgets: [], focusedWidget: undefined, selectedWidgetAncestory: [], + startPoints: undefined, }; export const widgetDraggingReducer = createImmerReducer(initialState, { @@ -28,11 +29,13 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { isDragging: boolean; dragGroupActualParent: string; draggingGroupCenter: string; + startPoints: any; }>, ) => { state.dragGroupActualParent = action.payload.dragGroupActualParent; state.isDragging = action.payload.isDragging; state.draggingGroupCenter = action.payload.draggingGroupCenter; + state.startPoints = action.payload.startPoints; }, [ReduxActionTypes.SET_NEW_WIDGET_DRAGGING]: ( state: WidgetDragResizeState, @@ -136,6 +139,7 @@ export type WidgetDragResizeState = { selectedWidgetAncestory: string[]; selectedWidgets: string[]; newWidget: any; + startPoints: any; }; export default widgetDraggingReducer; diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index e54e2ad95b3f..bdc9dd84a56a 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -1074,16 +1074,15 @@ export const noCollision = ( clientOffset: XYCoord, colWidth: number, rowHeight: number, - widget: WidgetProps & Partial, dropTargetOffset: XYCoord, + widgetWidth: number, + widgetHeight: number, + widgetId: string, occupiedSpaces?: OccupiedSpace[], rows?: number, cols?: number, ): boolean => { - if (clientOffset && dropTargetOffset && widget) { - if (widget.detachFromLayout) { - return true; - } + if (clientOffset && dropTargetOffset) { const [left, top] = getDropZoneOffsets( colWidth, rowHeight, @@ -1093,12 +1092,6 @@ export const noCollision = ( if (left < 0 || top < 0) { return false; } - const widgetWidth = widget.columns - ? widget.columns - : widget.rightColumn - widget.leftColumn; - const widgetHeight = widget.rows - ? widget.rows - : widget.bottomRow - widget.topRow; const currentOffset = { left, right: left + widgetWidth, @@ -1106,7 +1099,7 @@ export const noCollision = ( bottom: top + widgetHeight, }; return ( - !isDropZoneOccupied(currentOffset, widget.widgetId, occupiedSpaces) && + !isDropZoneOccupied(currentOffset, widgetId, occupiedSpaces) && !isWidgetOverflowingParentBounds({ rows, cols }, currentOffset) ); } diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index ec6149a55116..cb582ff24c16 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -51,28 +51,9 @@ export const Directions: { [id: string]: string } = { }; export type Direction = typeof Directions[keyof typeof Directions]; -const SCROLL_THESHOLD = 2.5; +const SCROLL_THRESHOLD = 2.5; export const getScrollByPixels = function( - elem: Element, - scrollParent: Element, -): number { - const bounding = elem.getBoundingClientRect(); - const scrollParentBounds = scrollParent.getBoundingClientRect(); - const scrollAmount = - GridDefaults.CANVAS_EXTENSION_OFFSET * GridDefaults.DEFAULT_GRID_ROW_HEIGHT; - - if ( - bounding.top > 0 && - bounding.top - scrollParentBounds.top < SCROLL_THESHOLD - ) - return -scrollAmount; - if (scrollParentBounds.bottom - bounding.bottom < SCROLL_THESHOLD) - return scrollAmount; - return 0; -}; - -export const getScrollByPixels2 = function( elem: { top: number; height: number; @@ -90,39 +71,21 @@ export const getScrollByPixels2 = function( elem.top + scrollChildBounds.top > 0 && elem.top + scrollChildBounds.top - - SCROLL_THESHOLD - + SCROLL_THRESHOLD - scrollParentBounds.top < - SCROLL_THESHOLD + SCROLL_THRESHOLD ) return -scrollAmount; if ( scrollParentBounds.bottom - - (elem.top + elem.height + scrollChildBounds.top + SCROLL_THESHOLD) < - SCROLL_THESHOLD + (elem.top + elem.height + scrollChildBounds.top + SCROLL_THRESHOLD) < + SCROLL_THRESHOLD ) return scrollAmount; return 0; }; export const scrollElementIntoParentCanvasView = ( - el: Element | null, - parent: Element | null, -) => { - if (el) { - const scrollParent = parent; - if (scrollParent) { - const scrollBy: number = getScrollByPixels(el, scrollParent); - if (scrollBy < 0 && scrollParent.scrollTop > 0) { - scrollParent.scrollBy({ top: scrollBy, behavior: "smooth" }); - } - if (scrollBy > 0) { - scrollParent.scrollBy({ top: scrollBy, behavior: "smooth" }); - } - } - } -}; - -export const scrollElementIntoParentCanvasView2 = ( el: { top: number; height: number; @@ -133,7 +96,7 @@ export const scrollElementIntoParentCanvasView2 = ( if (el) { const scrollParent = parent; if (scrollParent && child) { - const scrollBy: number = getScrollByPixels2(el, scrollParent, child); + const scrollBy: number = getScrollByPixels(el, scrollParent, child); if (scrollBy < 0 && scrollParent.scrollTop > 0) { scrollParent.scrollBy({ top: scrollBy, behavior: "smooth" }); } diff --git a/app/client/src/utils/hooks/dragResizeHooks.tsx b/app/client/src/utils/hooks/dragResizeHooks.tsx index fdb9192b5e8c..414b836921f9 100644 --- a/app/client/src/utils/hooks/dragResizeHooks.tsx +++ b/app/client/src/utils/hooks/dragResizeHooks.tsx @@ -114,6 +114,7 @@ export const useWidgetDragResize = () => { isDragging: boolean, dragGroupActualParent = "", draggingGroupCenter = "", + startPoints?: any, ) => { if (isDragging) { document.body.classList.add("dragging"); @@ -122,7 +123,12 @@ export const useWidgetDragResize = () => { } dispatch({ type: ReduxActionTypes.SET_WIDGET_DRAGGING, - payload: { isDragging, dragGroupActualParent, draggingGroupCenter }, + payload: { + isDragging, + dragGroupActualParent, + draggingGroupCenter, + startPoints, + }, }); }, [dispatch], diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index 35b8a3aff342..363eb5bf0047 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -124,16 +124,12 @@ class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { - // const childWidgets = (props.children || []).map((each) => { - // return each.widgetId; - // }); const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); return ( {props.type === "CANVAS_WIDGET" && ( Date: Wed, 7 Jul 2021 21:31:57 +0530 Subject: [PATCH 14/68] dip --- .../src/components/editorComponents/DraggableComponent.tsx | 4 ++++ app/client/src/pages/common/CanvasDraggingArena.tsx | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 6a3879ba7765..8016ee658be4 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -10,6 +10,7 @@ import { commentModeSelector } from "selectors/commentsSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { selectWidgetInitAction } from "actions/widgetSelectionActions"; import { useCallback } from "react"; +import { useEffect } from "react"; const DraggableWrapper = styled.div` display: block; @@ -148,6 +149,9 @@ function DraggableComponent(props: DraggableComponentProps) { top: props.bottomRow / 2, left: props.rightColumn / 2, }); + useEffect(() => { + mightBeDragging.current = false; + }, [isResizing]); const onMouseMove = useCallback( (e: any) => { if (draggableRef.current && mightBeDragging.current && allowDrag) { diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index fe05f56fed08..384e07c3ab3f 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -340,11 +340,13 @@ export function CanvasDraggingArena({ startPoints.left = ((dragParent === widgetId ? dragCenterSpace.left : 0) + dragStartPoints.left) * - snapColumnSpace; + snapColumnSpace + + (noPad ? 0 : 2 * CONTAINER_GRID_PADDING); startPoints.top = ((dragParent === widgetId ? dragCenterSpace.top : 0) + dragStartPoints.top) * - snapRowSpace; + snapRowSpace + + (noPad ? 0 : 2 * CONTAINER_GRID_PADDING); } canvasIsDragging = true; canvasRef.current.style.zIndex = "2"; From e81834797a1c1de46ed8dc144e060f69867137f4 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 7 Jul 2021 22:13:57 +0530 Subject: [PATCH 15/68] dip --- .../editorComponents/DraggableComponent.tsx | 60 +++++-------------- .../src/pages/common/CanvasDraggingArena.tsx | 44 +++++++------- 2 files changed, 40 insertions(+), 64 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 8016ee658be4..380d11b97cce 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -9,7 +9,6 @@ import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; import { commentModeSelector } from "selectors/commentsSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { selectWidgetInitAction } from "actions/widgetSelectionActions"; -import { useCallback } from "react"; import { useEffect } from "react"; const DraggableWrapper = styled.div` @@ -145,59 +144,32 @@ function DraggableComponent(props: DraggableComponentProps) { const dispatch = useDispatch(); const mightBeDragging = useRef(false); const draggableRef = useRef(null); - const startPoints = useRef({ - top: props.bottomRow / 2, - left: props.rightColumn / 2, - }); useEffect(() => { mightBeDragging.current = false; }, [isResizing]); - const onMouseMove = useCallback( - (e: any) => { - if (draggableRef.current && mightBeDragging.current && allowDrag) { - e.preventDefault(); - if (!selectedWidgets.includes(props.widgetId)) { - dispatch(selectWidgetInitAction(props.widgetId)); - } - mightBeDragging.current = false; - setDraggingState( - true, - props.parentId || "", - props.widgetId, - startPoints.current, - ); - e.stopPropagation(); - } - }, - [allowDrag, selectedWidgets, setDraggingState], - ); - const onMouseDown = useCallback( - (e: any) => { - if (!e.metaKey && allowDrag && draggableRef.current) { - mightBeDragging.current = true; - const bounds = draggableRef.current.getBoundingClientRect(); - startPoints.current = { - top: (e.clientY - bounds.top) / props.parentRowSpace, - left: (e.clientX - bounds.left) / props.parentColumnSpace, - }; - e.stopPropagation(); + const onDragStart = (e: any) => { + e.preventDefault(); + e.stopPropagation(); + if (draggableRef.current && !e.metaKey) { + if (!selectedWidgets.includes(props.widgetId)) { + dispatch(selectWidgetInitAction(props.widgetId)); } - }, - [allowDrag], - ); - - const onMouseUp = useCallback(() => { - mightBeDragging.current = false; - }, []); + const bounds = draggableRef.current.getBoundingClientRect(); + const startPoints = { + top: (e.clientY - bounds.top) / props.parentRowSpace, + left: (e.clientX - bounds.left) / props.parentColumnSpace, + }; + setDraggingState(true, props.parentId || "", props.widgetId, startPoints); + } + }; return ( diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 384e07c3ab3f..9085374a90b1 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -188,21 +188,23 @@ export function CanvasDraggingArena({ }[], rows: number, ) => { - const sortedByTopBlocks = drawingBlocks.sort( - (each1, each2) => each2.top + each2.height - (each1.top + each1.height), - ); - const bottomMostBlock = sortedByTopBlocks[0]; - const [, top] = getDropZoneOffsets( - snapColumnSpace, - snapRowSpace, - { - x: bottomMostBlock.left, - y: bottomMostBlock.top + bottomMostBlock.height, - } as XYCoord, - { x: 0, y: 0 }, - ); - if (top > rows - GridDefaults.CANVAS_EXTENSION_OFFSET) { - return updateDropTargetRows && updateDropTargetRows(widgetId, top); + if (drawingBlocks.length > 1) { + const sortedByTopBlocks = drawingBlocks.sort( + (each1, each2) => each2.top + each2.height - (each1.top + each1.height), + ); + const bottomMostBlock = sortedByTopBlocks[0]; + const [, top] = getDropZoneOffsets( + snapColumnSpace, + snapRowSpace, + { + x: bottomMostBlock.left, + y: bottomMostBlock.top + bottomMostBlock.height, + } as XYCoord, + { x: 0, y: 0 }, + ); + if (top > rows - GridDefaults.CANVAS_EXTENSION_OFFSET) { + return updateDropTargetRows && updateDropTargetRows(widgetId, top); + } } }; const { updateWidget } = useContext(EditorContext); @@ -259,7 +261,7 @@ export function CanvasDraggingArena({ }; useEffect(() => { - if (canvasRef.current && !isResizing) { + if (canvasRef.current && !isResizing && rectanglesToDraw.length > 0) { const scale = 1; let canvasIsDragging = false; @@ -329,7 +331,7 @@ export function CanvasDraggingArena({ } }; - const onMouseDown = () => { + const onMouseDown = (e: any) => { if ( !isResizing && isDragging && @@ -350,6 +352,7 @@ export function CanvasDraggingArena({ } canvasIsDragging = true; canvasRef.current.style.zIndex = "2"; + onMouseMove(e); } }; const onMouseMove = (e: any) => { @@ -404,7 +407,7 @@ export function CanvasDraggingArena({ scrollToKeepUp(newRectanglesToDraw); } else { - onMouseDown(); + onMouseDown(e); } }; let notDoneYet = false; @@ -510,7 +513,7 @@ export function CanvasDraggingArena({ canvasRef.current?.addEventListener("mouseover", onMouseDown, false); canvasRef.current?.addEventListener("mouseout", onMouseOut, false); canvasRef.current?.addEventListener("mouseleave", onMouseOut, false); - + document.body.addEventListener("mouseup", onMouseUp, false); if (canvasIsDragging) { // fix_dpi(); // drawDragLayer(rows); @@ -529,12 +532,13 @@ export function CanvasDraggingArena({ canvasRef.current?.removeEventListener("mouseover", onMouseDown); canvasRef.current?.removeEventListener("mouseout", onMouseOut); canvasRef.current?.removeEventListener("mouseleave", onMouseOut); + document.body.removeEventListener("mouseup", onMouseUp); }; } else { onMouseOut(); } } - }, [isDragging, newWidget, isResizing]); + }, [isDragging, newWidget, isResizing, rectanglesToDraw]); return isDragging && !isResizing ? ( Date: Thu, 8 Jul 2021 16:40:44 +0530 Subject: [PATCH 16/68] dip --- .../editorComponents/DraggableComponent.tsx | 2 +- .../editorComponents/DropTargetComponent.tsx | 2 ++ .../src/pages/common/CanvasDraggingArena.tsx | 18 +++++++----------- .../src/pages/common/CanvasSelectionArena.tsx | 9 ++++++++- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 380d11b97cce..b2895ae8f1c9 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -168,7 +168,7 @@ function DraggableComponent(props: DraggableComponentProps) { number | false; persistDropTargetRows?: (widgetId: string, row: number) => void; + rows?: number; }> = createContext({}); export function DropTargetComponent(props: DropTargetComponentProps) { @@ -153,6 +154,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { value={{ updateDropTargetRows, persistDropTargetRows, + rows, }} > (null); - const { persistDropTargetRows, updateDropTargetRows } = useContext( - DropTargetContext, - ); + const { + persistDropTargetRows, + rows = snapRows, + updateDropTargetRows, + } = useContext(DropTargetContext); const scrollToKeepUp = ( drawingBlocks: { @@ -137,7 +139,7 @@ export function CanvasDraggingArena({ ) => { if (isDragging) { let groupBlock; - if (drawingBlocks.length > 1) { + if (drawingBlocks.length) { const sortedByTopBlocks = drawingBlocks.sort( (each2, each1) => each2.top - each1.top, ); @@ -188,7 +190,7 @@ export function CanvasDraggingArena({ }[], rows: number, ) => { - if (drawingBlocks.length > 1) { + if (drawingBlocks.length) { const sortedByTopBlocks = drawingBlocks.sort( (each1, each2) => each2.top + each2.height - (each1.top + each1.height), ); @@ -275,8 +277,6 @@ export function CanvasDraggingArena({ } }; if (isDragging) { - let rows = snapRows; - // draggingCanvas.style.padding = `${noPad ? 0 : CONTAINER_GRID_PADDING}px`; const { height, width } = canvasRef.current.getBoundingClientRect(); canvasRef.current.width = width * scale; @@ -375,7 +375,6 @@ export function CanvasDraggingArena({ })); const newRows = updateRows(drawingBlocks, rows); const rowDiff = newRows ? newRows - rows : 0; - rows = newRows && newRows !== rows ? newRows : rows; newRectanglesToDraw = drawingBlocks.map((each) => ({ ...each, isNotColliding: noCollision( @@ -547,7 +546,4 @@ export function CanvasDraggingArena({ /> ) : null; } -CanvasDraggingArena.whyDidYouRender = { - logOnDifferentValues: true, -}; CanvasDraggingArena.displayName = "CanvasDraggingArena"; diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 0dcd4196126d..3da15dd86d1d 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -239,7 +239,14 @@ export const CanvasSelectionArena = memo( selectionCanvas.removeEventListener("click", onClick); }; } - }, [appLayout, currentPageId, mainContainer, isDragging]); + }, [ + appLayout, + currentPageId, + mainContainer, + isDragging, + mainContainer.bottomRow, + mainContainer.minHeight, + ]); return appMode === APP_MODE.EDIT && !isDragging ? ( Date: Thu, 8 Jul 2021 17:06:02 +0530 Subject: [PATCH 17/68] dip --- app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx | 10 ++++++++-- app/client/src/pages/common/CanvasDraggingArena.tsx | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx index 3f4f96ba844d..aa7217c72c29 100644 --- a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx +++ b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx @@ -21,6 +21,7 @@ import { getCanvasWidgets } from "selectors/entitiesSelector"; import { IPopoverSharedProps, Position } from "@blueprintjs/core"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { WidgetTypes } from "constants/WidgetConstants"; +import { AppState } from "reducers"; const StyledSelectionBox = styled.div` position: absolute; @@ -164,7 +165,9 @@ function WidgetsMultiSelectBox(props: { (widgetID) => canvasWidgets[widgetID], ); const { focusWidget } = useWidgetSelection(); - + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); /** * the multi-selection bounding box should only render when: * @@ -173,6 +176,9 @@ function WidgetsMultiSelectBox(props: { * 3. multiple widgets are selected */ const shouldRender = useMemo(() => { + if (isDragging) { + return false; + } const parentIDs = selectedWidgets .filter(Boolean) .map((widget) => widget.parentId); @@ -185,7 +191,7 @@ function WidgetsMultiSelectBox(props: { hasCommonParent && get(selectedWidgets, "0.parentId") === props.widgetId ); - }, [selectedWidgets]); + }, [selectedWidgets, isDragging]); /** * calculate bounding box diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 4b5c4744c108..161cbd802e16 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -318,6 +318,9 @@ export function CanvasDraggingArena({ top: 20, }; const onMouseUp = () => { + if (isDragging && canvasIsDragging) { + onDrop(newRectanglesToDraw); + } startPoints.left = 20; startPoints.top = 20; if (newWidget) { @@ -326,9 +329,6 @@ export function CanvasDraggingArena({ setDraggingState(false); } onMouseOut(); - if (isDragging && canvasIsDragging) { - onDrop(newRectanglesToDraw); - } }; const onMouseDown = (e: any) => { From 9f74062023fb64e3791cbe538fc97be8d804c732 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 8 Jul 2021 19:39:41 +0530 Subject: [PATCH 18/68] dip --- .../editorComponents/DraggableComponent.tsx | 7 +++- .../pages/Editor/WidgetsMultiSelectBox.tsx | 38 ++++++++++++++++++- .../src/pages/common/CanvasDraggingArena.tsx | 12 ++++-- .../reducers/uiReducers/dragResizeReducer.ts | 12 ++++-- .../src/utils/hooks/dragResizeHooks.tsx | 2 +- app/client/src/widgets/ContainerWidget.tsx | 2 +- 6 files changed, 63 insertions(+), 10 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index b2895ae8f1c9..86b27c611682 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -160,7 +160,12 @@ function DraggableComponent(props: DraggableComponentProps) { top: (e.clientY - bounds.top) / props.parentRowSpace, left: (e.clientX - bounds.left) / props.parentColumnSpace, }; - setDraggingState(true, props.parentId || "", props.widgetId, startPoints); + setDraggingState( + true, + props.parentId || "", + { widgetId: props.widgetId }, + startPoints, + ); } }; diff --git a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx index aa7217c72c29..1825cb27aa6a 100644 --- a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx +++ b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from "react"; +import React, { useMemo, useRef } from "react"; import styled from "styled-components"; import { get, minBy, maxBy } from "lodash"; import { useSelector, useDispatch } from "react-redux"; @@ -22,6 +22,7 @@ import { IPopoverSharedProps, Position } from "@blueprintjs/core"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { WidgetTypes } from "constants/WidgetConstants"; import { AppState } from "reducers"; +import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; const StyledSelectionBox = styled.div` position: absolute; @@ -192,6 +193,38 @@ function WidgetsMultiSelectBox(props: { get(selectedWidgets, "0.parentId") === props.widgetId ); }, [selectedWidgets, isDragging]); + const draggableRef = useRef(null); + const { setDraggingState } = useWidgetDragResize(); + + const onDragStart = (e: any) => { + e.preventDefault(); + e.stopPropagation(); + if (draggableRef.current) { + const bounds = draggableRef.current.getBoundingClientRect(); + const parentRowSpace = get(selectedWidgets, "0.parentRowSpace"); + const parentColumnSpace = get(selectedWidgets, "0.parentColumnSpace"); + const parentId = get(selectedWidgets, "0.parentId"); + const startPoints = { + top: (e.clientY - bounds.top) / parentRowSpace, + left: (e.clientX - bounds.left) / parentColumnSpace, + }; + const top = selectedWidgets.sort((a1, a2) => a1.topRow - a2.topRow)[0] + .topRow; + const left = selectedWidgets.sort( + (a1, a2) => a1.leftColumn - a2.leftColumn, + )[0].leftColumn; + + setDraggingState( + true, + parentId || "", + { + top, + left, + }, + startPoints, + ); + } + }; /** * calculate bounding box @@ -273,9 +306,12 @@ function WidgetsMultiSelectBox(props: { return ( focusWidget()} onMouseOver={() => focusWidget()} + ref={draggableRef} style={{ left: left?.left, top: top?.top, diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 161cbd802e16..2fd910fea465 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -88,9 +88,15 @@ export function CanvasDraggingArena({ const dragCenter = useSelector( (state: AppState) => state.ui.widgetDragResize.draggingGroupCenter, ); - const dragCenterSpace = childrenOccupiedSpaces.find( - (each) => each.id === dragCenter, - ); + + const dragCenterSpace: any = + dragCenter && dragCenter.widgetId + ? childrenOccupiedSpaces.find( + (each) => each.id === dragCenter.widgetId, + ) || {} + : dragCenter && !!dragCenter.top && dragCenter.left + ? dragCenter + : {}; const rectanglesToDraw = !newWidget ? childrenOccupiedSpaces .filter((each) => selectedWidgets.includes(each.id)) diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index 062396633288..db2c6a78cd10 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -7,7 +7,7 @@ const initialState: WidgetDragResizeState = { isDragging: false, isResizing: false, dragGroupActualParent: "", - draggingGroupCenter: "", + draggingGroupCenter: {}, newWidget: undefined, lastSelectedWidget: undefined, selectedWidgets: [], @@ -28,7 +28,7 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { action: ReduxAction<{ isDragging: boolean; dragGroupActualParent: string; - draggingGroupCenter: string; + draggingGroupCenter: DraggingGroupCenter; startPoints: any; }>, ) => { @@ -128,11 +128,17 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { }, }); +type DraggingGroupCenter = { + widgetId?: string; + top?: number; + left?: number; +}; + export type WidgetDragResizeState = { isDraggingDisabled: boolean; isDragging: boolean; dragGroupActualParent: string; - draggingGroupCenter: string; + draggingGroupCenter: DraggingGroupCenter; isResizing: boolean; lastSelectedWidget?: string; focusedWidget?: string; diff --git a/app/client/src/utils/hooks/dragResizeHooks.tsx b/app/client/src/utils/hooks/dragResizeHooks.tsx index 414b836921f9..755f68561710 100644 --- a/app/client/src/utils/hooks/dragResizeHooks.tsx +++ b/app/client/src/utils/hooks/dragResizeHooks.tsx @@ -113,7 +113,7 @@ export const useWidgetDragResize = () => { ( isDragging: boolean, dragGroupActualParent = "", - draggingGroupCenter = "", + draggingGroupCenter = {}, startPoints?: any, ) => { if (isDragging) { diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index 363eb5bf0047..52955cc59b33 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -135,11 +135,11 @@ class ContainerWidget extends BaseWidget< widgetId={props.widgetId} /> )} + - {/* without the wrapping div onClick events are triggered twice */} <>{this.renderChildren()} From 7e47395d99c9fbb9243f92b3830b976eebdcf321 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 8 Jul 2021 20:21:36 +0530 Subject: [PATCH 19/68] dip --- .../src/pages/common/CanvasDraggingArena.tsx | 8 +- .../src/pages/common/CanvasSelectionArena.tsx | 406 +++++++++--------- app/client/src/widgets/ContainerWidget.tsx | 20 +- 3 files changed, 225 insertions(+), 209 deletions(-) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 2fd910fea465..8a6802d221ad 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -23,7 +23,7 @@ import { scrollElementIntoParentCanvasView } from "utils/helpers"; import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; import { getWidgets } from "sagas/selectors"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; -import { debounce, throttle } from "lodash"; +import { debounce, isEmpty, throttle } from "lodash"; const StyledSelectionCanvas = styled.canvas` position: absolute; @@ -291,14 +291,14 @@ export function CanvasDraggingArena({ const differentParent = dragParent !== widgetId; const parentDiff = { top: - differentParent && dragCenterSpace + differentParent && !isEmpty(dragCenterSpace) ? dragCenterSpace.top * snapRowSpace + (noPad ? 0 : CONTAINER_GRID_PADDING) : noPad ? 0 : CONTAINER_GRID_PADDING, left: - differentParent && dragCenterSpace + differentParent && !isEmpty(dragCenterSpace) ? dragCenterSpace.left * snapColumnSpace + (noPad ? 0 : CONTAINER_GRID_PADDING) : noPad @@ -344,7 +344,7 @@ export function CanvasDraggingArena({ !canvasIsDragging && canvasRef.current ) { - if (dragCenterSpace) { + if (!isEmpty(dragCenterSpace)) { startPoints.left = ((dragParent === widgetId ? dragCenterSpace.left : 0) + dragStartPoints.left) * diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 3da15dd86d1d..0924802af4bb 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -3,7 +3,7 @@ import { setCanvasSelectionStateAction, } from "actions/canvasSelectionActions"; import { throttle } from "lodash"; -import React, { memo, useEffect, useCallback } from "react"; +import React, { useEffect, useCallback } from "react"; import { useDispatch, useSelector } from "react-redux"; import { AppState } from "reducers"; import { APP_MODE } from "reducers/entityReducers/appReducer"; @@ -35,225 +35,235 @@ export interface SelectedArenaDimensions { height: number; } -export const CanvasSelectionArena = memo( - ({ widgetId }: { widgetId: string }) => { - const dispatch = useDispatch(); - const appMode = useSelector(getAppMode); - const isDragging = useSelector( - (state: AppState) => state.ui.widgetDragResize.isDragging, - ); - const mainContainer = useSelector((state: AppState) => - getWidget(state, widgetId), - ); - const currentPageId = useSelector(getCurrentPageId); - const appLayout = useSelector(getCurrentApplicationLayout); - const throttledWidgetSelection = useCallback( - throttle( - ( - selectionDimensions: SelectedArenaDimensions, - snapToNextColumn: boolean, - snapToNextRow: boolean, - isMultiSelect: boolean, - ) => { - dispatch( - selectAllWidgetsInAreaAction( - selectionDimensions, - snapToNextColumn, - snapToNextRow, - isMultiSelect, - ), - ); - }, - 150, - { - leading: true, - trailing: true, - }, - ), - [widgetId], - ); - useEffect(() => { - if (appMode === APP_MODE.EDIT && !isDragging) { - const selectionCanvas: any = document.getElementById( - `canvas-${widgetId}`, +export function CanvasSelectionArena({ + snapRows, + snapRowSpace, + widgetId, +}: { + widgetId: string; + snapRows: number; + snapRowSpace: number; +}) { + const dispatch = useDispatch(); + const canvasRef = React.useRef(null); + + const appMode = useSelector(getAppMode); + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); + const mainContainer = useSelector((state: AppState) => + getWidget(state, widgetId), + ); + const currentPageId = useSelector(getCurrentPageId); + const appLayout = useSelector(getCurrentApplicationLayout); + const throttledWidgetSelection = useCallback( + throttle( + ( + selectionDimensions: SelectedArenaDimensions, + snapToNextColumn: boolean, + snapToNextRow: boolean, + isMultiSelect: boolean, + ) => { + dispatch( + selectAllWidgetsInAreaAction( + selectionDimensions, + snapToNextColumn, + snapToNextRow, + isMultiSelect, + ), ); - const scale = 1; + }, + 150, + { + leading: true, + trailing: true, + }, + ), + [widgetId], + ); + useEffect(() => { + if (appMode === APP_MODE.EDIT && !isDragging && canvasRef.current) { + const scale = 1; - const canvasCtx = selectionCanvas.getContext("2d"); - const initRectangle = (): SelectedArenaDimensions => ({ - top: 0, - left: 0, - width: 0, - height: 0, - }); - let selectionRectangle: SelectedArenaDimensions = initRectangle(); - let isMultiSelect = false; - let isDragging = false; + let canvasCtx: any = canvasRef.current.getContext("2d"); + const initRectangle = (): SelectedArenaDimensions => ({ + top: 0, + left: 0, + width: 0, + height: 0, + }); + let selectionRectangle: SelectedArenaDimensions = initRectangle(); + let isMultiSelect = false; + let isDragging = false; - const init = () => { - const { height, width } = selectionCanvas.getBoundingClientRect(); + const init = () => { + if (canvasRef.current) { + const { height, width } = canvasRef.current.getBoundingClientRect(); if (height && width) { - selectionCanvas.width = width; - selectionCanvas.height = height; + canvasRef.current.width = width; + canvasRef.current.height = + snapRows * snapRowSpace + (widgetId === "0" ? 200 : 0); } + canvasCtx = canvasRef.current.getContext("2d"); canvasCtx.scale(scale, scale); - selectionCanvas.addEventListener("click", onClick, false); - selectionCanvas.addEventListener("mousedown", onMouseDown, false); - selectionCanvas.addEventListener("mouseup", onMouseUp, false); - selectionCanvas.addEventListener("mousemove", onMouseMove, false); - selectionCanvas.addEventListener("mouseleave", onMouseLeave, false); - selectionCanvas.addEventListener("mouseenter", onMouseEnter, false); - }; - - const getSelectionDimensions = () => { - return { - top: - selectionRectangle.height < 0 - ? selectionRectangle.top - Math.abs(selectionRectangle.height) - : selectionRectangle.top, - left: - selectionRectangle.width < 0 - ? selectionRectangle.left - Math.abs(selectionRectangle.width) - : selectionRectangle.left, - width: Math.abs(selectionRectangle.width), - height: Math.abs(selectionRectangle.height), - }; - }; + canvasRef.current.addEventListener("click", onClick, false); + canvasRef.current.addEventListener("mousedown", onMouseDown, false); + canvasRef.current.addEventListener("mouseup", onMouseUp, false); + canvasRef.current.addEventListener("mousemove", onMouseMove, false); + canvasRef.current.addEventListener("mouseleave", onMouseLeave, false); + canvasRef.current.addEventListener("mouseenter", onMouseEnter, false); + } + }; - const selectWidgetsInit = ( - selectionDimensions: SelectedArenaDimensions, - isMultiSelect: boolean, - ) => { - if ( - selectionDimensions.left && - selectionDimensions.top && - selectionDimensions.width && - selectionDimensions.height - ) { - const snapToNextColumn = selectionRectangle.height < 0; - const snapToNextRow = selectionRectangle.width < 0; - throttledWidgetSelection( - selectionDimensions, - snapToNextColumn, - snapToNextRow, - isMultiSelect, - ); - } + const getSelectionDimensions = () => { + return { + top: + selectionRectangle.height < 0 + ? selectionRectangle.top - Math.abs(selectionRectangle.height) + : selectionRectangle.top, + left: + selectionRectangle.width < 0 + ? selectionRectangle.left - Math.abs(selectionRectangle.width) + : selectionRectangle.left, + width: Math.abs(selectionRectangle.width), + height: Math.abs(selectionRectangle.height), }; + }; - const drawRectangle = ( - selectionDimensions: SelectedArenaDimensions, - ) => { - const strokeWidth = 1; - canvasCtx.setLineDash([5]); - canvasCtx.strokeStyle = "rgb(84, 132, 236)"; - canvasCtx.strokeRect( - selectionDimensions.left - strokeWidth, - selectionDimensions.top - strokeWidth, - selectionDimensions.width + 2 * strokeWidth, - selectionDimensions.height + 2 * strokeWidth, + const selectWidgetsInit = ( + selectionDimensions: SelectedArenaDimensions, + isMultiSelect: boolean, + ) => { + if ( + selectionDimensions.left && + selectionDimensions.top && + selectionDimensions.width && + selectionDimensions.height + ) { + const snapToNextColumn = selectionRectangle.height < 0; + const snapToNextRow = selectionRectangle.width < 0; + throttledWidgetSelection( + selectionDimensions, + snapToNextColumn, + snapToNextRow, + isMultiSelect, ); - canvasCtx.fillStyle = "rgb(84, 132, 236, 0.06)"; - canvasCtx.fillRect( - selectionDimensions.left, - selectionDimensions.top, - selectionDimensions.width, - selectionDimensions.height, - ); - }; + } + }; - const onMouseLeave = () => { - document.body.addEventListener("mouseup", onMouseUp, false); - document.body.addEventListener("click", onClick, false); - }; + const drawRectangle = (selectionDimensions: SelectedArenaDimensions) => { + const strokeWidth = 1; + canvasCtx.setLineDash([5]); + canvasCtx.strokeStyle = "rgb(84, 132, 236)"; + canvasCtx.strokeRect( + selectionDimensions.left - strokeWidth, + selectionDimensions.top - strokeWidth, + selectionDimensions.width + 2 * strokeWidth, + selectionDimensions.height + 2 * strokeWidth, + ); + canvasCtx.fillStyle = "rgb(84, 132, 236, 0.06)"; + canvasCtx.fillRect( + selectionDimensions.left, + selectionDimensions.top, + selectionDimensions.width, + selectionDimensions.height, + ); + }; - const onMouseEnter = () => { - document.body.removeEventListener("mouseup", onMouseUp); - document.body.removeEventListener("click", onClick); - }; + const onMouseLeave = () => { + document.body.addEventListener("mouseup", onMouseUp, false); + document.body.addEventListener("click", onClick, false); + }; + + const onMouseEnter = () => { + document.body.removeEventListener("mouseup", onMouseUp); + document.body.removeEventListener("click", onClick); + }; - const onClick = (e: any) => { - if ( - Math.abs(selectionRectangle.height) + - Math.abs(selectionRectangle.width) > - 0 - ) { - if (!isDragging) { - // cant set this in onMouseUp coz click seems to happen after onMouseUp. - selectionRectangle = initRectangle(); - } - e.stopPropagation(); + const onClick = (e: any) => { + if ( + Math.abs(selectionRectangle.height) + + Math.abs(selectionRectangle.width) > + 0 + ) { + if (!isDragging) { + // cant set this in onMouseUp coz click seems to happen after onMouseUp. + selectionRectangle = initRectangle(); } - }; + e.stopPropagation(); + } + }; - const onMouseDown = (e: any) => { + const onMouseDown = (e: any) => { + if (canvasRef.current) { isMultiSelect = e.ctrlKey || e.metaKey || e.shiftKey; - selectionRectangle.left = e.offsetX - selectionCanvas.offsetLeft; - selectionRectangle.top = e.offsetY - selectionCanvas.offsetTop; + selectionRectangle.left = e.offsetX - canvasRef.current.offsetLeft; + selectionRectangle.top = e.offsetY - canvasRef.current.offsetTop; selectionRectangle.width = 0; selectionRectangle.height = 0; isDragging = true; dispatch(setCanvasSelectionStateAction(true, widgetId)); // bring the canvas to the top layer - selectionCanvas.style.zIndex = 2; - }; - const onMouseUp = () => { - if (isDragging) { - isDragging = false; - canvasCtx.clearRect( - 0, - 0, - selectionCanvas.width, - selectionCanvas.height, - ); - selectionCanvas.style.zIndex = null; - dispatch(setCanvasSelectionStateAction(false, widgetId)); - } - }; - const onMouseMove = (e: any) => { - if (isDragging) { - selectionRectangle.width = - e.offsetX - selectionCanvas.offsetLeft - selectionRectangle.left; - selectionRectangle.height = - e.offsetY - selectionCanvas.offsetTop - selectionRectangle.top; - canvasCtx.clearRect( - 0, - 0, - selectionCanvas.width, - selectionCanvas.height, - ); - const selectionDimensions = getSelectionDimensions(); - drawRectangle(selectionDimensions); - selectWidgetsInit(selectionDimensions, isMultiSelect); - } - }; - if (appMode === APP_MODE.EDIT) { - init(); + canvasRef.current.style.zIndex = "2"; } - return () => { - selectionCanvas.removeEventListener("mousedown", onMouseDown); - selectionCanvas.removeEventListener("mouseup", onMouseUp); - selectionCanvas.removeEventListener("mousemove", onMouseMove); - selectionCanvas.removeEventListener("mouseleave", onMouseLeave); - selectionCanvas.removeEventListener("mouseenter", onMouseEnter); - selectionCanvas.removeEventListener("click", onClick); - }; + }; + const onMouseUp = () => { + if (isDragging && canvasRef.current) { + isDragging = false; + canvasCtx.clearRect( + 0, + 0, + canvasRef.current.width, + canvasRef.current.height, + ); + canvasRef.current.style.zIndex = ""; + dispatch(setCanvasSelectionStateAction(false, widgetId)); + } + }; + const onMouseMove = (e: any) => { + if (isDragging && canvasRef.current) { + selectionRectangle.width = + e.offsetX - canvasRef.current.offsetLeft - selectionRectangle.left; + selectionRectangle.height = + e.offsetY - canvasRef.current.offsetTop - selectionRectangle.top; + canvasCtx.clearRect( + 0, + 0, + canvasRef.current.width, + canvasRef.current.height, + ); + const selectionDimensions = getSelectionDimensions(); + drawRectangle(selectionDimensions); + selectWidgetsInit(selectionDimensions, isMultiSelect); + } + }; + if (appMode === APP_MODE.EDIT) { + init(); } - }, [ - appLayout, - currentPageId, - mainContainer, - isDragging, - mainContainer.bottomRow, - mainContainer.minHeight, - ]); + return () => { + canvasRef.current?.removeEventListener("mousedown", onMouseDown); + canvasRef.current?.removeEventListener("mouseup", onMouseUp); + canvasRef.current?.removeEventListener("mousemove", onMouseMove); + canvasRef.current?.removeEventListener("mouseleave", onMouseLeave); + canvasRef.current?.removeEventListener("mouseenter", onMouseEnter); + canvasRef.current?.removeEventListener("click", onClick); + }; + } + }, [ + appLayout, + currentPageId, + mainContainer, + isDragging, + snapRows, + mainContainer.minHeight, + ]); - return appMode === APP_MODE.EDIT && !isDragging ? ( - - ) : null; - }, -); + return appMode === APP_MODE.EDIT && !isDragging ? ( + + ) : null; +} CanvasSelectionArena.displayName = "CanvasSelectionArena"; diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index 52955cc59b33..8a108f9e5b91 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -128,14 +128,20 @@ class ContainerWidget extends BaseWidget< return ( {props.type === "CANVAS_WIDGET" && ( - + <> + + + )} - Date: Fri, 9 Jul 2021 13:17:59 +0530 Subject: [PATCH 20/68] dip --- app/client/src/pages/common/CanvasDraggingArena.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 8a6802d221ad..90908a1a68b7 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -519,6 +519,8 @@ export function CanvasDraggingArena({ canvasRef.current?.addEventListener("mouseout", onMouseOut, false); canvasRef.current?.addEventListener("mouseleave", onMouseOut, false); document.body.addEventListener("mouseup", onMouseUp, false); + window.addEventListener("mouseup", onMouseUp, false); + if (canvasIsDragging) { // fix_dpi(); // drawDragLayer(rows); @@ -538,6 +540,7 @@ export function CanvasDraggingArena({ canvasRef.current?.removeEventListener("mouseout", onMouseOut); canvasRef.current?.removeEventListener("mouseleave", onMouseOut); document.body.removeEventListener("mouseup", onMouseUp); + window.removeEventListener("mouseup", onMouseUp, false); }; } else { onMouseOut(); From 5c2554a0ddf720355c3c16943b829051b5a4f815 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 13 Jul 2021 11:02:48 +0530 Subject: [PATCH 21/68] dip --- .../editorComponents/DraggableComponent.tsx | 3 +- .../editorComponents/DropTargetComponent.tsx | 44 +++-- .../src/constants/ReduxActionConstants.tsx | 1 + .../src/pages/common/CanvasDraggingArena.tsx | 159 +++++++++++------- .../src/pages/common/CanvasSelectionArena.tsx | 10 +- .../reducers/uiReducers/dragResizeReducer.ts | 10 ++ .../src/utils/hooks/dragResizeHooks.tsx | 11 ++ 7 files changed, 158 insertions(+), 80 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 86b27c611682..90c7fe919c91 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -66,7 +66,7 @@ function DraggableComponent(props: DraggableComponentProps) { // Dispatch hook handy to set any `DraggableComponent` as dragging/ not dragging // The value is boolean - const { setDraggingState } = useWidgetDragResize(); + const { setDraggingCanvas, setDraggingState } = useWidgetDragResize(); const selectedWidgets = useSelector( (state: AppState) => state.ui.widgetDragResize.selectedWidgets, @@ -160,6 +160,7 @@ function DraggableComponent(props: DraggableComponentProps) { top: (e.clientY - bounds.top) / props.parentRowSpace, left: (e.clientX - bounds.left) / props.parentColumnSpace, }; + setDraggingCanvas(props.parentId); setDraggingState( true, props.parentId || "", diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 47fcff8f12bc..0e6d180da2de 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -1,10 +1,11 @@ import React, { - useState, ReactNode, Context, createContext, memo, useEffect, + useRef, + useCallback, } from "react"; import styled from "styled-components"; import { WidgetProps } from "widgets/BaseWidget"; @@ -23,6 +24,7 @@ import { } from "utils/hooks/dragResizeHooks"; import { getOccupiedSpaces } from "selectors/editorSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { debounce } from "lodash"; type DropTargetComponentProps = WidgetProps & { children?: ReactNode; @@ -74,6 +76,10 @@ export function DropTargetComponent(props: DropTargetComponentProps) { (state: AppState) => state.ui.widgetDragResize.isDragging, ); + const draggedOn = useSelector( + (state: AppState) => state.ui.widgetDragResize.draggedOn, + ); + const childWidgets = useSelector( (state: AppState) => state.entities.canvasWidgets[props.widgetId].children, ); @@ -81,7 +87,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const occupiedSpacesByChildren = occupiedSpaces && occupiedSpaces[props.widgetId]; - const [rows, setRows] = useState(snapRows); + const rowRef = useRef(snapRows); const showPropertyPane = useShowPropertyPane(); const { deselectAll, focusWidget } = useWidgetSelection(); @@ -89,7 +95,8 @@ export function DropTargetComponent(props: DropTargetComponentProps) { useEffect(() => { const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); - setRows(snapRows); + rowRef.current = snapRows; + updateHeight(); }, [props.bottomRow, props.canExtend]); const persistDropTargetRows = (widgetId: string, widgetBottomRow: number) => { @@ -103,11 +110,26 @@ export function DropTargetComponent(props: DropTargetComponentProps) { props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1, newRows, ); - setRows(rowsToPersist); + rowRef.current = rowsToPersist; + updateHeight(); if (canDropTargetExtend) { updateCanvasSnapRows(props.widgetId, rowsToPersist); } }; + const updateHeight = useCallback( + debounce(() => { + if (dropTargetRef.current) { + const height = canDropTargetExtend + ? `${Math.max( + rowRef.current * props.snapRowSpace, + props.minHeight, + )}px` + : "100%"; + dropTargetRef.current.style.height = height; + } + }), + [], + ); /* Update the rows of the main container based on the current widget's (dragging/resizing) bottom row */ const updateDropTargetRows = (widgetId: string, widgetBottomRow: number) => { @@ -119,8 +141,9 @@ export function DropTargetComponent(props: DropTargetComponentProps) { occupiedSpacesByChildren, ); - if (rows < newRows) { - setRows(newRows); + if (rowRef.current < newRows) { + rowRef.current = newRows; + updateHeight(); return newRows; } return false; @@ -141,25 +164,24 @@ export function DropTargetComponent(props: DropTargetComponentProps) { e.preventDefault(); }; const height = canDropTargetExtend - ? `${Math.max(rows * props.snapRowSpace, props.minHeight)}px` + ? `${Math.max(rowRef.current * props.snapRowSpace, props.minHeight)}px` : "100%"; - const boxShadow = (isResizing || isDragging) && props.widgetId === MAIN_CONTAINER_WIDGET_ID ? "0px 0px 0px 1px #DDDDDD" : "0px 0px 0px 1px transparent"; - + const dropTargetRef = useRef(null); return ( } - {(isDragging || isResizing) && ( + {(isDragging || isResizing) && draggedOn === props.widgetId && ( state.ui.widgetDragResize.isDragging, ); + const draggedOn = useSelector( + (state: AppState) => state.ui.widgetDragResize.draggedOn, + ); const dragStartPoints = useSelector( (state: AppState) => state.ui.widgetDragResize.startPoints, ); @@ -123,14 +126,22 @@ export function CanvasDraggingArena({ isNotColliding: true, }, ]; - const { setDraggingNewWidget, setDraggingState } = useWidgetDragResize(); + const { + setDraggingCanvas, + setDraggingNewWidget, + setDraggingState, + } = useWidgetDragResize(); const canvasRef = React.useRef(null); - const { - persistDropTargetRows, - rows = snapRows, - updateDropTargetRows, - } = useContext(DropTargetContext); + + const { persistDropTargetRows, updateDropTargetRows } = useContext( + DropTargetContext, + ); + + const rowRef = useRef(snapRows); + useEffect(() => { + rowRef.current = snapRows; + }, [snapRows]); const scrollToKeepUp = ( drawingBlocks: { @@ -270,16 +281,19 @@ export function CanvasDraggingArena({ useEffect(() => { if (canvasRef.current && !isResizing && rectanglesToDraw.length > 0) { - const scale = 1; + const { devicePixelRatio: scale = 1 } = window; let canvasIsDragging = false; + let notDoneYet = false; + let animationFrameId: number; const onMouseOut = () => { if (canvasRef.current) { const { height, width } = canvasRef.current.getBoundingClientRect(); const canvasCtx: any = canvasRef.current.getContext("2d"); - canvasRef.current.style.zIndex = ""; canvasCtx.clearRect(0, 0, width * scale, height * scale); + canvasRef.current.style.zIndex = ""; canvasIsDragging = false; + setDraggingCanvas(); } }; if (isDragging) { @@ -315,7 +329,7 @@ export function CanvasDraggingArena({ widgetId: string; isNotColliding: boolean; }[] = []; - let canvasCtx: any = canvasRef.current.getContext("2d"); + const canvasCtx: any = canvasRef.current.getContext("2d"); canvasCtx.globalCompositeOperation = "destination-over"; canvasCtx.scale(scale, scale); @@ -329,12 +343,15 @@ export function CanvasDraggingArena({ } startPoints.left = 20; startPoints.top = 20; - if (newWidget) { - setDraggingNewWidget(false, undefined); - } else { - setDraggingState(false); - } + const wasCanvasDragging = canvasIsDragging; onMouseOut(); + if (wasCanvasDragging) { + if (newWidget) { + setDraggingNewWidget(false, undefined); + } else { + setDraggingState(false); + } + } }; const onMouseDown = (e: any) => { @@ -356,6 +373,9 @@ export function CanvasDraggingArena({ snapRowSpace + (noPad ? 0 : 2 * CONTAINER_GRID_PADDING); } + if (draggedOn !== widgetId) { + setDraggingCanvas(widgetId); + } canvasIsDragging = true; canvasRef.current.style.zIndex = "2"; onMouseMove(e); @@ -379,8 +399,9 @@ export function CanvasDraggingArena({ left: each.left + diff.left, top: each.top + diff.top, })); - const newRows = updateRows(drawingBlocks, rows); - const rowDiff = newRows ? newRows - rows : 0; + const newRows = updateRows(drawingBlocks, rowRef.current); + const rowDiff = newRows ? newRows - rowRef.current : 0; + rowRef.current = newRows ? newRows : rowRef.current; newRectanglesToDraw = drawingBlocks.map((each) => ({ ...each, isNotColliding: noCollision( @@ -392,7 +413,7 @@ export function CanvasDraggingArena({ each.rowHeight, each.widgetId, occSpaces, - rows, + rowRef.current, GridDefaults.DEFAULT_GRID_COLUMNS, ), })); @@ -400,14 +421,7 @@ export function CanvasDraggingArena({ notDoneYet = true; drawInit(rowDiff, diff); } else if (!notDoneYet) { - const { - height, - width, - } = canvasRef.current.getBoundingClientRect(); - canvasCtx.clearRect(0, 0, width * scale, height * scale); - newRectanglesToDraw.forEach((each) => { - drawRectangle(each); - }); + drawBlocks(); } scrollToKeepUp(newRectanglesToDraw); @@ -415,47 +429,52 @@ export function CanvasDraggingArena({ onMouseDown(e); } }; - let notDoneYet = false; - const drawInit = throttle( - debounce( - (rowDiff, diff) => { - notDoneYet = true; - if (canvasRef.current) { - newRectanglesToDraw = rectanglesToDraw.map((each) => { - return { - ...each, - left: each.left + diff.left, - top: each.top + diff.top, - }; - }); - - canvasRef.current.height = - rows * snapRowSpace + (widgetId === "0" ? 200 : 0) * scale; - canvasCtx = canvasRef.current.getContext("2d"); - canvasCtx.scale(scale, scale); - const { - height, - width, - } = canvasRef.current.getBoundingClientRect(); - // drawDragLayer(rows); + const drawInit = debounce((rowDiff, diff) => { + notDoneYet = true; + if (canvasRef.current) { + newRectanglesToDraw = rectanglesToDraw.map((each) => { + return { + ...each, + left: each.left + diff.left, + top: each.top + diff.top, + }; + }); + canvasCtx.save(); + canvasRef.current.height = + (rowRef.current * snapRowSpace + (widgetId === "0" ? 200 : 0)) * + scale; + // canvasCtx = canvasRef.current.getContext("2d"); + canvasCtx.scale(scale, scale); + canvasCtx.clearRect(0, 0, width, canvasRef.current.height); + canvasCtx.restore(); + drawBlocks(); + // scrollToKeepUp(newRectanglesToDraw); + } + }); - canvasCtx.clearRect(0, 0, width * scale, height * scale); - notDoneYet = false; + const drawBlocks = debounce( + () => { + if (canvasRef.current) { + const canvasCtx: any = canvasRef.current.getContext("2d"); + const { + height, + width, + } = canvasRef.current.getBoundingClientRect(); + canvasCtx.save(); + canvasCtx.clearRect(0, 0, width * scale, height * scale); + notDoneYet = false; + if (canvasIsDragging) { newRectanglesToDraw.forEach((each) => { drawRectangle(each); }); - // scrollToKeepUp(newRectanglesToDraw); } - }, - 10, - { - leading: false, - trailing: true, - }, - ), - 10, + canvasCtx.restore(); + animationFrameId = window.requestAnimationFrame(drawBlocks); + } + }, + 0, { - leading: false, + leading: true, trailing: true, }, ); @@ -534,6 +553,7 @@ export function CanvasDraggingArena({ canvasRef.current.style.zIndex = "2"; } return () => { + window.cancelAnimationFrame(animationFrameId); canvasRef.current?.removeEventListener("mousemove", onMouseMove); canvasRef.current?.removeEventListener("mouseup", onMouseUp); canvasRef.current?.removeEventListener("mouseover", onMouseDown); @@ -546,7 +566,17 @@ export function CanvasDraggingArena({ onMouseOut(); } } - }, [isDragging, newWidget, isResizing, rectanglesToDraw]); + }, [isDragging, newWidget, isResizing, rectanglesToDraw, snapRows]); + + useEffect(() => { + if (draggedOn !== widgetId && canvasRef.current) { + const { devicePixelRatio: scale = 1 } = window; + const { height, width } = canvasRef.current.getBoundingClientRect(); + const canvasCtx: any = canvasRef.current.getContext("2d"); + canvasCtx.clearRect(0, 0, width * scale, height * scale); + } + }, [draggedOn]); + return isDragging && !isResizing ? ( ) : null; } +CanvasDraggingArena.whyDidYouRender = { + logOnDifferentValues: true, +}; CanvasDraggingArena.displayName = "CanvasDraggingArena"; diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 0924802af4bb..1b17f75b791b 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -83,7 +83,7 @@ export function CanvasSelectionArena({ ); useEffect(() => { if (appMode === APP_MODE.EDIT && !isDragging && canvasRef.current) { - const scale = 1; + const { devicePixelRatio: scale = 1 } = window; let canvasCtx: any = canvasRef.current.getContext("2d"); const initRectangle = (): SelectedArenaDimensions => ({ @@ -100,9 +100,9 @@ export function CanvasSelectionArena({ if (canvasRef.current) { const { height, width } = canvasRef.current.getBoundingClientRect(); if (height && width) { - canvasRef.current.width = width; + canvasRef.current.width = width * scale; canvasRef.current.height = - snapRows * snapRowSpace + (widgetId === "0" ? 200 : 0); + (snapRows * snapRowSpace + (widgetId === "0" ? 200 : 0)) * scale; } canvasCtx = canvasRef.current.getContext("2d"); canvasCtx.scale(scale, scale); @@ -195,7 +195,7 @@ export function CanvasSelectionArena({ }; const onMouseDown = (e: any) => { - if (canvasRef.current) { + if (canvasRef.current && (widgetId === "0" || e.ctrlKey || e.metaKey)) { isMultiSelect = e.ctrlKey || e.metaKey || e.shiftKey; selectionRectangle.left = e.offsetX - canvasRef.current.offsetLeft; selectionRectangle.top = e.offsetY - canvasRef.current.offsetTop; @@ -255,7 +255,7 @@ export function CanvasSelectionArena({ mainContainer, isDragging, snapRows, - mainContainer.minHeight, + // mainContainer.minHeight, ]); return appMode === APP_MODE.EDIT && !isDragging ? ( diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index db2c6a78cd10..7271acbc0f2e 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -14,6 +14,7 @@ const initialState: WidgetDragResizeState = { focusedWidget: undefined, selectedWidgetAncestory: [], startPoints: undefined, + draggedOn: undefined, }; export const widgetDraggingReducer = createImmerReducer(initialState, { @@ -23,6 +24,14 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { ) => { state.isDraggingDisabled = action.payload.isDraggingDisabled; }, + [ReduxActionTypes.SET_DRAGGING_CANVAS]: ( + state: WidgetDragResizeState, + action: ReduxAction<{ + draggedOn: string; + }>, + ) => { + state.draggedOn = action.payload.draggedOn; + }, [ReduxActionTypes.SET_WIDGET_DRAGGING]: ( state: WidgetDragResizeState, action: ReduxAction<{ @@ -146,6 +155,7 @@ export type WidgetDragResizeState = { selectedWidgets: string[]; newWidget: any; startPoints: any; + draggedOn?: string; }; export default widgetDraggingReducer; diff --git a/app/client/src/utils/hooks/dragResizeHooks.tsx b/app/client/src/utils/hooks/dragResizeHooks.tsx index 755f68561710..394b6ef087e0 100644 --- a/app/client/src/utils/hooks/dragResizeHooks.tsx +++ b/app/client/src/utils/hooks/dragResizeHooks.tsx @@ -133,6 +133,17 @@ export const useWidgetDragResize = () => { }, [dispatch], ), + setDraggingCanvas: useCallback( + (draggedOn?: string) => { + dispatch({ + type: ReduxActionTypes.SET_DRAGGING_CANVAS, + payload: { + draggedOn, + }, + }); + }, + [dispatch], + ), setIsResizing: useCallback( (isResizing: boolean) => { dispatch({ From 9a4834836837973a1c4fbef0c5ee0926477bf86f Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 13 Jul 2021 11:10:14 +0530 Subject: [PATCH 22/68] dip --- .../src/pages/common/CanvasDraggingArena.tsx | 47 ++++++------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index f7e49de1458f..262340653e79 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -87,7 +87,6 @@ export function CanvasDraggingArena({ (state: AppState) => state.ui.widgetDragResize.newWidget, ); const allWidgets = useSelector(getWidgets); - // const widget = useSelector((state: AppState) => getWidget(state, widgetId)); const dragCenter = useSelector( (state: AppState) => state.ui.widgetDragResize.draggingGroupCenter, ); @@ -260,7 +259,6 @@ export function CanvasDraggingArena({ widget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : widgetId, ); - // const widgetBottomRow = getWidgetBottomRow(widget, updateWidgetParams); const widgetBottomRow = updateWidgetParams.payload.topRow + (updateWidgetParams.payload.rows || @@ -297,7 +295,6 @@ export function CanvasDraggingArena({ } }; if (isDragging) { - // draggingCanvas.style.padding = `${noPad ? 0 : CONTAINER_GRID_PADDING}px`; const { height, width } = canvasRef.current.getBoundingClientRect(); canvasRef.current.width = width * scale; canvasRef.current.height = height * scale; @@ -443,41 +440,29 @@ export function CanvasDraggingArena({ canvasRef.current.height = (rowRef.current * snapRowSpace + (widgetId === "0" ? 200 : 0)) * scale; - // canvasCtx = canvasRef.current.getContext("2d"); canvasCtx.scale(scale, scale); canvasCtx.clearRect(0, 0, width, canvasRef.current.height); canvasCtx.restore(); drawBlocks(); - // scrollToKeepUp(newRectanglesToDraw); } }); - const drawBlocks = debounce( - () => { - if (canvasRef.current) { - const canvasCtx: any = canvasRef.current.getContext("2d"); - const { - height, - width, - } = canvasRef.current.getBoundingClientRect(); - canvasCtx.save(); - canvasCtx.clearRect(0, 0, width * scale, height * scale); - notDoneYet = false; - if (canvasIsDragging) { - newRectanglesToDraw.forEach((each) => { - drawRectangle(each); - }); - } - canvasCtx.restore(); - animationFrameId = window.requestAnimationFrame(drawBlocks); + const drawBlocks = () => { + if (canvasRef.current) { + const canvasCtx: any = canvasRef.current.getContext("2d"); + const { height, width } = canvasRef.current.getBoundingClientRect(); + canvasCtx.save(); + canvasCtx.clearRect(0, 0, width * scale, height * scale); + notDoneYet = false; + if (canvasIsDragging) { + newRectanglesToDraw.forEach((each) => { + drawRectangle(each); + }); } - }, - 0, - { - leading: true, - trailing: true, - }, - ); + canvasCtx.restore(); + animationFrameId = window.requestAnimationFrame(drawBlocks); + } + }; const drawRectangle = (selectionDimensions: { top: number; @@ -541,8 +526,6 @@ export function CanvasDraggingArena({ window.addEventListener("mouseup", onMouseUp, false); if (canvasIsDragging) { - // fix_dpi(); - // drawDragLayer(rows); rectanglesToDraw.forEach((each) => { drawRectangle(each); }); From a8201e49e559d15f03e8c5ee5e97b023729edf24 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 13 Jul 2021 12:53:58 +0530 Subject: [PATCH 23/68] solve for canvas glitches --- .../src/pages/common/CanvasDraggingArena.tsx | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 262340653e79..a4003151832d 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -291,7 +291,6 @@ export function CanvasDraggingArena({ canvasCtx.clearRect(0, 0, width * scale, height * scale); canvasRef.current.style.zIndex = ""; canvasIsDragging = false; - setDraggingCanvas(); } }; if (isDragging) { @@ -348,6 +347,7 @@ export function CanvasDraggingArena({ } else { setDraggingState(false); } + setDraggingCanvas(); } }; @@ -448,7 +448,7 @@ export function CanvasDraggingArena({ }); const drawBlocks = () => { - if (canvasRef.current) { + if (canvasRef.current && draggedOn === widgetId) { const canvasCtx: any = canvasRef.current.getContext("2d"); const { height, width } = canvasRef.current.getBoundingClientRect(); canvasCtx.save(); @@ -543,7 +543,7 @@ export function CanvasDraggingArena({ canvasRef.current?.removeEventListener("mouseout", onMouseOut); canvasRef.current?.removeEventListener("mouseleave", onMouseOut); document.body.removeEventListener("mouseup", onMouseUp); - window.removeEventListener("mouseup", onMouseUp, false); + window.removeEventListener("mouseup", onMouseUp); }; } else { onMouseOut(); @@ -551,15 +551,6 @@ export function CanvasDraggingArena({ } }, [isDragging, newWidget, isResizing, rectanglesToDraw, snapRows]); - useEffect(() => { - if (draggedOn !== widgetId && canvasRef.current) { - const { devicePixelRatio: scale = 1 } = window; - const { height, width } = canvasRef.current.getBoundingClientRect(); - const canvasCtx: any = canvasRef.current.getContext("2d"); - canvasCtx.clearRect(0, 0, width * scale, height * scale); - } - }, [draggedOn]); - return isDragging && !isResizing ? ( Date: Thu, 15 Jul 2021 11:59:20 +0530 Subject: [PATCH 24/68] dip --- .../pages/Editor/WidgetsMultiSelectBox.tsx | 8 +- .../src/pages/common/CanvasDraggingArena.tsx | 151 +++++++++++------- app/client/src/sagas/SelectionCanvasSagas.ts | 9 +- app/client/src/utils/helpers.tsx | 44 +++-- 4 files changed, 131 insertions(+), 81 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx index 1825cb27aa6a..5b5d53255fc4 100644 --- a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx +++ b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx @@ -208,11 +208,9 @@ function WidgetsMultiSelectBox(props: { top: (e.clientY - bounds.top) / parentRowSpace, left: (e.clientX - bounds.left) / parentColumnSpace, }; - const top = selectedWidgets.sort((a1, a2) => a1.topRow - a2.topRow)[0] - .topRow; - const left = selectedWidgets.sort( - (a1, a2) => a1.leftColumn - a2.leftColumn, - )[0].leftColumn; + const top = minBy(selectedWidgets, (rect) => rect.topRow)?.topRow; + const left = minBy(selectedWidgets, (rect) => rect.leftColumn) + ?.leftColumn; setDraggingState( true, diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index a4003151832d..37abba1309aa 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -19,11 +19,11 @@ import { } from "utils/WidgetPropsUtils"; import { getSnappedXY } from "components/editorComponents/Dropzone"; import { getNearestParentCanvas } from "utils/generators"; -import { scrollElementIntoParentCanvasView } from "utils/helpers"; +import { getScrollByPixels } from "utils/helpers"; import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; import { getWidgets } from "sagas/selectors"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; -import { debounce, isEmpty } from "lodash"; +import { debounce, defer, isEmpty } from "lodash"; const StyledSelectionCanvas = styled.canvas` position: absolute; @@ -96,7 +96,9 @@ export function CanvasDraggingArena({ ? childrenOccupiedSpaces.find( (each) => each.id === dragCenter.widgetId, ) || {} - : dragCenter && !!dragCenter.top && dragCenter.left + : dragCenter && + Number.isInteger(dragCenter.top) && + Number.isInteger(dragCenter.left) ? dragCenter : {}; const rectanglesToDraw = !newWidget @@ -140,59 +142,7 @@ export function CanvasDraggingArena({ const rowRef = useRef(snapRows); useEffect(() => { rowRef.current = snapRows; - }, [snapRows]); - - const scrollToKeepUp = ( - drawingBlocks: { - left: number; - top: number; - width: number; - height: number; - columnWidth: number; - rowHeight: number; - widgetId: string; - }[], - ) => { - if (isDragging) { - let groupBlock; - if (drawingBlocks.length) { - const sortedByTopBlocks = drawingBlocks.sort( - (each2, each1) => each2.top - each1.top, - ); - const topMostBlock = sortedByTopBlocks[0]; - const sortedByBottomBlocks = drawingBlocks.sort( - (each1, each2) => - each2.top + each2.height - (each1.top + each1.height), - ); - const bottomMostBlock = sortedByBottomBlocks[0]; - groupBlock = { - top: topMostBlock.top, - height: - bottomMostBlock.top - topMostBlock.top + bottomMostBlock.height, - }; - } else { - const block = drawingBlocks[0]; - groupBlock = { - top: block.top, - height: block.height, - }; - } - - const scrollParent: Element | null = getNearestParentCanvas( - canvasRef.current, - ); - if (canvasRef.current) { - // if (el && props.canDropTargetExtend) { - if (groupBlock) { - scrollElementIntoParentCanvasView( - groupBlock, - scrollParent, - canvasRef.current, - ); - } - } - } - }; + }, [snapRows, isDragging]); const updateRows = ( drawingBlocks: { @@ -284,12 +234,86 @@ export function CanvasDraggingArena({ let canvasIsDragging = false; let notDoneYet = false; let animationFrameId: number; + let scrollTimeOut: number[] = []; + let scrollDirection = 0; + let scrollByPixels = 0; + const scrollFn = (scrollParent: Element | null) => { + if (isDragging && draggedOn === widgetId && scrollParent) { + if (scrollByPixels < 0 && scrollParent.scrollTop > 0) { + scrollParent.scrollBy({ + top: scrollByPixels, + behavior: "smooth", + }); + } + if (scrollByPixels > 0) { + scrollParent.scrollBy({ + top: scrollByPixels, + behavior: "smooth", + }); + } + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } + scrollTimeOut.push(setTimeout(() => scrollFn(scrollParent), 50)); + } else { + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } + } + }; + const checkIfNeedsScroll = debounce((e: any) => { + if (isDragging && canvasIsDragging && draggedOn === widgetId) { + const scrollParent: Element | null = getNearestParentCanvas( + canvasRef.current, + ); + if (canvasRef.current && scrollParent) { + scrollByPixels = getScrollByPixels( + { + top: e.offsetY, + height: 0, + }, + scrollParent, + canvasRef.current, + ); + const currentScrollDirection = scrollByPixels + ? scrollByPixels > 0 + ? 1 + : -1 + : 0; + if (currentScrollDirection !== scrollDirection) { + scrollDirection = currentScrollDirection; + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } + if (!!scrollDirection) { + scrollFn(scrollParent); + } + } + } + } + }); const onMouseOut = () => { if (canvasRef.current) { + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } const { height, width } = canvasRef.current.getBoundingClientRect(); const canvasCtx: any = canvasRef.current.getContext("2d"); canvasCtx.clearRect(0, 0, width * scale, height * scale); canvasRef.current.style.zIndex = ""; + scrollDirection = 0; canvasIsDragging = false; } }; @@ -337,6 +361,12 @@ export function CanvasDraggingArena({ if (isDragging && canvasIsDragging) { onDrop(newRectanglesToDraw); } + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } startPoints.left = 20; startPoints.top = 20; const wasCanvasDragging = canvasIsDragging; @@ -420,8 +450,7 @@ export function CanvasDraggingArena({ } else if (!notDoneYet) { drawBlocks(); } - - scrollToKeepUp(newRectanglesToDraw); + checkIfNeedsScroll(e); } else { onMouseDown(e); } @@ -536,6 +565,12 @@ export function CanvasDraggingArena({ canvasRef.current.style.zIndex = "2"; } return () => { + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } window.cancelAnimationFrame(animationFrameId); canvasRef.current?.removeEventListener("mousemove", onMouseMove); canvasRef.current?.removeEventListener("mouseup", onMouseUp); diff --git a/app/client/src/sagas/SelectionCanvasSagas.ts b/app/client/src/sagas/SelectionCanvasSagas.ts index 7d973639076f..af30d5d5e24d 100644 --- a/app/client/src/sagas/SelectionCanvasSagas.ts +++ b/app/client/src/sagas/SelectionCanvasSagas.ts @@ -124,6 +124,9 @@ function* startCanvasSelectionSaga( getWidget, actionPayload.payload.widgetId, ); + const lastSelectedWidgetsWithoutParent = lastSelectedWidgets.filter( + (each) => each !== mainContainer.parentId, + ); const widgetOccupiedSpaces: | { [containerWidgetId: string]: OccupiedSpace[]; @@ -132,7 +135,11 @@ function* startCanvasSelectionSaga( const selectionTask = yield takeLatest( ReduxActionTypes.SELECT_WIDGETS_IN_AREA, selectAllWidgetsInAreaSaga, - { lastSelectedWidgets, mainContainer, widgetOccupiedSpaces }, + { + lastSelectedWidgets: lastSelectedWidgetsWithoutParent, + mainContainer, + widgetOccupiedSpaces, + }, ); yield take(ReduxActionTypes.STOP_CANVAS_SELECTION); yield cancel(selectionTask); diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index cb582ff24c16..385b514a6bb6 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -51,7 +51,7 @@ export const Directions: { [id: string]: string } = { }; export type Direction = typeof Directions[keyof typeof Directions]; -const SCROLL_THRESHOLD = 2.5; +const SCROLL_THRESHOLD = 20; export const getScrollByPixels = function( elem: { @@ -65,23 +65,33 @@ export const getScrollByPixels = function( const scrollParentBounds = scrollParent.getBoundingClientRect(); const scrollChildBounds = child.getBoundingClientRect(); const scrollAmount = - GridDefaults.CANVAS_EXTENSION_OFFSET * GridDefaults.DEFAULT_GRID_ROW_HEIGHT; - - if ( - elem.top + scrollChildBounds.top > 0 && - elem.top + - scrollChildBounds.top - - SCROLL_THRESHOLD - - scrollParentBounds.top < - SCROLL_THRESHOLD - ) - return -scrollAmount; - if ( + 2 * + GridDefaults.CANVAS_EXTENSION_OFFSET * + GridDefaults.DEFAULT_GRID_ROW_HEIGHT; + const topBuff = + elem.top + scrollChildBounds.top > 0 + ? elem.top + + scrollChildBounds.top - + SCROLL_THRESHOLD - + scrollParentBounds.top + : 0; + const bottomBuff = scrollParentBounds.bottom - - (elem.top + elem.height + scrollChildBounds.top + SCROLL_THRESHOLD) < - SCROLL_THRESHOLD - ) - return scrollAmount; + (elem.top + elem.height + scrollChildBounds.top + SCROLL_THRESHOLD); + if (topBuff < SCROLL_THRESHOLD) { + const speed = Math.max( + (SCROLL_THRESHOLD - topBuff) / (2 * SCROLL_THRESHOLD), + 0.1, + ); + return -(scrollAmount * speed); + } + if (bottomBuff < SCROLL_THRESHOLD) { + const speed = Math.max( + (SCROLL_THRESHOLD - bottomBuff) / (2 * SCROLL_THRESHOLD), + 0.1, + ); + return scrollAmount * speed; + } return 0; }; From 91bc015fd45851544deb6fd9bf7c8b0647c2bb05 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 15 Jul 2021 12:04:46 +0530 Subject: [PATCH 25/68] dip --- app/client/src/pages/common/CanvasDraggingArena.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 37abba1309aa..e2d5713386ae 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -23,7 +23,7 @@ import { getScrollByPixels } from "utils/helpers"; import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; import { getWidgets } from "sagas/selectors"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; -import { debounce, defer, isEmpty } from "lodash"; +import { debounce, isEmpty } from "lodash"; const StyledSelectionCanvas = styled.canvas` position: absolute; From 4f016a3e7be599690b0fb53db13a8310905abc97 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 15 Jul 2021 12:36:43 +0530 Subject: [PATCH 26/68] dip --- app/client/src/sagas/WidgetOperationSagas.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index efc37242f9ce..6ab1642e576c 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -311,10 +311,17 @@ export function* addChildSaga(addChildAction: ReduxAction) { addChildAction.payload, widgets, ); + const newWidget = childWidgetPayload.widgets[childWidgetPayload.widgetId]; + + const updateBottomRow = + newWidget.bottomRow * newWidget.parentRowSpace > stateParent.bottomRow; // Update widgets to put back in the canvasWidgetsReducer // TODO(abhinav): This won't work if dont already have an empty children: [] const parent = { ...stateParent, + ...(updateBottomRow + ? { bottomRow: newWidget.bottomRow * newWidget.parentRowSpace } + : {}), children: [...(stateParent.children || []), childWidgetPayload.widgetId], }; From 2458ace31ae579f498d435cd361d4a14b2a01099 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 15 Jul 2021 15:03:34 +0530 Subject: [PATCH 27/68] adjust scroll speed --- app/client/src/pages/common/CanvasDraggingArena.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index e2d5713386ae..9cb88bfb7689 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -257,7 +257,7 @@ export function CanvasDraggingArena({ }); scrollTimeOut = []; } - scrollTimeOut.push(setTimeout(() => scrollFn(scrollParent), 50)); + scrollTimeOut.push(setTimeout(() => scrollFn(scrollParent), 100)); } else { if (scrollTimeOut.length) { scrollTimeOut.forEach((each) => { From 6cf5990b4420746df064410193ebd1f9a01b12e3 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 15 Jul 2021 16:08:01 +0530 Subject: [PATCH 28/68] dip --- .../src/pages/common/CanvasDraggingArena.tsx | 23 +++++++++------- app/client/src/utils/helpers.tsx | 26 +++++++++++++++---- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 9cb88bfb7689..41e62f96fe2d 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -237,15 +237,13 @@ export function CanvasDraggingArena({ let scrollTimeOut: number[] = []; let scrollDirection = 0; let scrollByPixels = 0; + let speed = 0; const scrollFn = (scrollParent: Element | null) => { if (isDragging && draggedOn === widgetId && scrollParent) { - if (scrollByPixels < 0 && scrollParent.scrollTop > 0) { - scrollParent.scrollBy({ - top: scrollByPixels, - behavior: "smooth", - }); - } - if (scrollByPixels > 0) { + if ( + (scrollByPixels < 0 && scrollParent.scrollTop > 0) || + scrollByPixels > 0 + ) { scrollParent.scrollBy({ top: scrollByPixels, behavior: "smooth", @@ -257,7 +255,12 @@ export function CanvasDraggingArena({ }); scrollTimeOut = []; } - scrollTimeOut.push(setTimeout(() => scrollFn(scrollParent), 100)); + scrollTimeOut.push( + setTimeout( + () => scrollFn(scrollParent), + 100 * Math.max(0.4, speed), + ), + ); } else { if (scrollTimeOut.length) { scrollTimeOut.forEach((each) => { @@ -273,7 +276,7 @@ export function CanvasDraggingArena({ canvasRef.current, ); if (canvasRef.current && scrollParent) { - scrollByPixels = getScrollByPixels( + const scrollObj = getScrollByPixels( { top: e.offsetY, height: 0, @@ -281,6 +284,8 @@ export function CanvasDraggingArena({ scrollParent, canvasRef.current, ); + scrollByPixels = scrollObj.scrollAmount; + speed = scrollObj.speed; const currentScrollDirection = scrollByPixels ? scrollByPixels > 0 ? 1 diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index 385b514a6bb6..0bc4c6714619 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -60,7 +60,10 @@ export const getScrollByPixels = function( }, scrollParent: Element, child: Element, -): number { +): { + scrollAmount: number; + speed: number; +} { // const bounding = elem.getBoundingClientRect(); const scrollParentBounds = scrollParent.getBoundingClientRect(); const scrollChildBounds = child.getBoundingClientRect(); @@ -83,16 +86,25 @@ export const getScrollByPixels = function( (SCROLL_THRESHOLD - topBuff) / (2 * SCROLL_THRESHOLD), 0.1, ); - return -(scrollAmount * speed); + return { + scrollAmount: 0 - scrollAmount, + speed, + }; } if (bottomBuff < SCROLL_THRESHOLD) { const speed = Math.max( (SCROLL_THRESHOLD - bottomBuff) / (2 * SCROLL_THRESHOLD), 0.1, ); - return scrollAmount * speed; + return { + scrollAmount, + speed, + }; } - return 0; + return { + scrollAmount: 0, + speed: 0, + }; }; export const scrollElementIntoParentCanvasView = ( @@ -106,7 +118,11 @@ export const scrollElementIntoParentCanvasView = ( if (el) { const scrollParent = parent; if (scrollParent && child) { - const scrollBy: number = getScrollByPixels(el, scrollParent, child); + const { scrollAmount: scrollBy } = getScrollByPixels( + el, + scrollParent, + child, + ); if (scrollBy < 0 && scrollParent.scrollTop > 0) { scrollParent.scrollBy({ top: scrollBy, behavior: "smooth" }); } From 859fff42c7c0e88ea7ab3f5ee272444fc5f7ebed Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 15 Jul 2021 19:45:34 +0530 Subject: [PATCH 29/68] dip(code clean up) --- .../editorComponents/DropTargetComponent.tsx | 6 +- .../Editor/Explorer/Widgets/WidgetEntity.tsx | 2 +- .../src/pages/common/CanvasDraggingArena.tsx | 597 +----------------- .../reducers/uiReducers/dragResizeReducer.ts | 39 +- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 242 +++++++ .../src/utils/hooks/useCanvasDragging.ts | 384 +++++++++++ 6 files changed, 680 insertions(+), 590 deletions(-) create mode 100644 app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts create mode 100644 app/client/src/utils/hooks/useCanvasDragging.ts diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 0e6d180da2de..b272c3605950 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -76,10 +76,12 @@ export function DropTargetComponent(props: DropTargetComponentProps) { (state: AppState) => state.ui.widgetDragResize.isDragging, ); - const draggedOn = useSelector( - (state: AppState) => state.ui.widgetDragResize.draggedOn, + const dragDetails = useSelector( + (state: AppState) => state.ui.widgetDragResize.dragDetails, ); + const { draggedOn } = dragDetails; + const childWidgets = useSelector( (state: AppState) => state.entities.canvasWidgets[props.widgetId].children, ); diff --git a/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx b/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx index a94ebaf13be0..4390bd3b88e8 100644 --- a/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx +++ b/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx @@ -89,7 +89,7 @@ export type WidgetEntityProps = { export const WidgetEntity = memo((props: WidgetEntityProps) => { const { pageId } = useParams(); const widgetsToExpand = useSelector( - (state: AppState) => state.ui.widgetDragResize.selectedWidgetAncestory, + (state: AppState) => state.ui.widgetDragResize.selectedWidgetAncestry, ); let shouldExpand = false; if (widgetsToExpand.includes(props.widgetId)) shouldExpand = true; diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 41e62f96fe2d..1ea9ca91fc89 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -1,41 +1,14 @@ -import { OccupiedSpace } from "constants/editorConstants"; -import { - CONTAINER_GRID_PADDING, - GridDefaults, - MAIN_CONTAINER_WIDGET_ID, -} from "constants/WidgetConstants"; -import React, { useContext, useEffect, useRef } from "react"; -import { useSelector } from "react-redux"; -import { AppState } from "reducers"; -import { getOccupiedSpaces } from "selectors/editorSelectors"; -import { getSelectedWidgets } from "selectors/ui"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import React, { useMemo } from "react"; import styled from "styled-components"; -import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; -import { XYCoord } from "react-dnd"; -import { - getDropZoneOffsets, - noCollision, - widgetOperationParams, -} from "utils/WidgetPropsUtils"; -import { getSnappedXY } from "components/editorComponents/Dropzone"; -import { getNearestParentCanvas } from "utils/generators"; -import { getScrollByPixels } from "utils/helpers"; -import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; -import { getWidgets } from "sagas/selectors"; -import { EditorContext } from "components/editorComponents/EditorContextProvider"; -import { debounce, isEmpty } from "lodash"; +import { theme } from "constants/DefaultTheme"; +import { useCanvasDragging } from "utils/hooks/useCanvasDragging"; -const StyledSelectionCanvas = styled.canvas` +const StyledSelectionCanvas = styled.canvas<{ paddingBottom: number }>` position: absolute; top: 0px; left: 0px; - height: calc( - 100% + - ${(props) => - props.id === "canvas-dragging-0" - ? props.theme.canvasBottomPadding - : 0}px - ); + height: calc(100% + ${(props) => props.paddingBottom}px); width: 100%; image-rendering: -moz-crisp-edges; image-rendering: -webkit-crisp-edges; @@ -51,555 +24,41 @@ export interface SelectedArenaDimensions { height: number; } -export function CanvasDraggingArena({ - noPad, - snapColumnSpace, - snapRows, - snapRowSpace, - widgetId, -}: { +export interface CanvasDraggingArenaProps { noPad?: boolean; snapColumnSpace: number; snapRows: number; snapRowSpace: number; widgetId: string; -}) { - const dragParent = useSelector( - (state: AppState) => state.ui.widgetDragResize.dragGroupActualParent, - ); - const isResizing = useSelector( - (state: AppState) => state.ui.widgetDragResize.isResizing, - ); - const selectedWidgets = useSelector(getSelectedWidgets); - const occupiedSpaces = useSelector(getOccupiedSpaces) || {}; - const childrenOccupiedSpaces: OccupiedSpace[] = - occupiedSpaces[dragParent] || []; - const isDragging = useSelector( - (state: AppState) => state.ui.widgetDragResize.isDragging, - ); - const draggedOn = useSelector( - (state: AppState) => state.ui.widgetDragResize.draggedOn, - ); - const dragStartPoints = useSelector( - (state: AppState) => state.ui.widgetDragResize.startPoints, - ); - const newWidget = useSelector( - (state: AppState) => state.ui.widgetDragResize.newWidget, - ); - const allWidgets = useSelector(getWidgets); - const dragCenter = useSelector( - (state: AppState) => state.ui.widgetDragResize.draggingGroupCenter, - ); +} - const dragCenterSpace: any = - dragCenter && dragCenter.widgetId - ? childrenOccupiedSpaces.find( - (each) => each.id === dragCenter.widgetId, - ) || {} - : dragCenter && - Number.isInteger(dragCenter.top) && - Number.isInteger(dragCenter.left) - ? dragCenter - : {}; - const rectanglesToDraw = !newWidget - ? childrenOccupiedSpaces - .filter((each) => selectedWidgets.includes(each.id)) - .map((each) => ({ - top: each.top * snapRowSpace + (noPad ? 0 : CONTAINER_GRID_PADDING), - left: - each.left * snapColumnSpace + (noPad ? 0 : CONTAINER_GRID_PADDING), - width: (each.right - each.left) * snapColumnSpace, - height: (each.bottom - each.top) * snapRowSpace, - columnWidth: each.right - each.left, - rowHeight: each.bottom - each.top, - widgetId: each.id, - isNotColliding: true, - })) - : [ - { - top: 0, - left: 0, - width: newWidget.columns * snapColumnSpace, - height: newWidget.rows * snapRowSpace, - columnWidth: newWidget.columns, - rowHeight: newWidget.rows, - widgetId: newWidget.widgetId, - isNotColliding: true, - }, - ]; - const { - setDraggingCanvas, - setDraggingNewWidget, - setDraggingState, - } = useWidgetDragResize(); +export function CanvasDraggingArena({ + noPad, + snapColumnSpace, + snapRows, + snapRowSpace, + widgetId, +}: CanvasDraggingArenaProps) { + const needsPadding = useMemo(() => { + return widgetId === MAIN_CONTAINER_WIDGET_ID; + }, [widgetId]); const canvasRef = React.useRef(null); - - const { persistDropTargetRows, updateDropTargetRows } = useContext( - DropTargetContext, - ); - - const rowRef = useRef(snapRows); - useEffect(() => { - rowRef.current = snapRows; - }, [snapRows, isDragging]); - - const updateRows = ( - drawingBlocks: { - left: number; - top: number; - width: number; - height: number; - columnWidth: number; - rowHeight: number; - widgetId: string; - }[], - rows: number, - ) => { - if (drawingBlocks.length) { - const sortedByTopBlocks = drawingBlocks.sort( - (each1, each2) => each2.top + each2.height - (each1.top + each1.height), - ); - const bottomMostBlock = sortedByTopBlocks[0]; - const [, top] = getDropZoneOffsets( - snapColumnSpace, - snapRowSpace, - { - x: bottomMostBlock.left, - y: bottomMostBlock.top + bottomMostBlock.height, - } as XYCoord, - { x: 0, y: 0 }, - ); - if (top > rows - GridDefaults.CANVAS_EXTENSION_OFFSET) { - return updateDropTargetRows && updateDropTargetRows(widgetId, top); - } - } - }; - const { updateWidget } = useContext(EditorContext); - - const onDrop = ( - drawingBlocks: { - left: number; - top: number; - width: number; - height: number; - columnWidth: number; - rowHeight: number; - widgetId: string; - isNotColliding: boolean; - }[], - ) => { - const cannotDrop = drawingBlocks.some((each) => { - return !each.isNotColliding; - }); - if (!cannotDrop) { - drawingBlocks - .sort( - (each1, each2) => - each1.top + each1.height - (each2.top + each2.height), - ) - .forEach((each) => { - const widget = newWidget ? newWidget : allWidgets[each.widgetId]; - const updateWidgetParams = widgetOperationParams( - widget, - { x: each.left, y: each.top }, - { x: 0, y: 0 }, - snapColumnSpace, - snapRowSpace, - widget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : widgetId, - ); - - const widgetBottomRow = - updateWidgetParams.payload.topRow + - (updateWidgetParams.payload.rows || - widget.bottomRow - widget.topRow); - persistDropTargetRows && - persistDropTargetRows(widget.widgetId, widgetBottomRow); - - /* Finally update the widget */ - updateWidget && - updateWidget( - updateWidgetParams.operation, - updateWidgetParams.widgetId, - updateWidgetParams.payload, - ); - }); - } - }; - - useEffect(() => { - if (canvasRef.current && !isResizing && rectanglesToDraw.length > 0) { - const { devicePixelRatio: scale = 1 } = window; - - let canvasIsDragging = false; - let notDoneYet = false; - let animationFrameId: number; - let scrollTimeOut: number[] = []; - let scrollDirection = 0; - let scrollByPixels = 0; - let speed = 0; - const scrollFn = (scrollParent: Element | null) => { - if (isDragging && draggedOn === widgetId && scrollParent) { - if ( - (scrollByPixels < 0 && scrollParent.scrollTop > 0) || - scrollByPixels > 0 - ) { - scrollParent.scrollBy({ - top: scrollByPixels, - behavior: "smooth", - }); - } - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } - scrollTimeOut.push( - setTimeout( - () => scrollFn(scrollParent), - 100 * Math.max(0.4, speed), - ), - ); - } else { - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } - } - }; - const checkIfNeedsScroll = debounce((e: any) => { - if (isDragging && canvasIsDragging && draggedOn === widgetId) { - const scrollParent: Element | null = getNearestParentCanvas( - canvasRef.current, - ); - if (canvasRef.current && scrollParent) { - const scrollObj = getScrollByPixels( - { - top: e.offsetY, - height: 0, - }, - scrollParent, - canvasRef.current, - ); - scrollByPixels = scrollObj.scrollAmount; - speed = scrollObj.speed; - const currentScrollDirection = scrollByPixels - ? scrollByPixels > 0 - ? 1 - : -1 - : 0; - if (currentScrollDirection !== scrollDirection) { - scrollDirection = currentScrollDirection; - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } - if (!!scrollDirection) { - scrollFn(scrollParent); - } - } - } - } - }); - const onMouseOut = () => { - if (canvasRef.current) { - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } - const { height, width } = canvasRef.current.getBoundingClientRect(); - const canvasCtx: any = canvasRef.current.getContext("2d"); - canvasCtx.clearRect(0, 0, width * scale, height * scale); - canvasRef.current.style.zIndex = ""; - scrollDirection = 0; - canvasIsDragging = false; - } - }; - if (isDragging) { - const { height, width } = canvasRef.current.getBoundingClientRect(); - canvasRef.current.width = width * scale; - canvasRef.current.height = height * scale; - - const differentParent = dragParent !== widgetId; - const parentDiff = { - top: - differentParent && !isEmpty(dragCenterSpace) - ? dragCenterSpace.top * snapRowSpace + - (noPad ? 0 : CONTAINER_GRID_PADDING) - : noPad - ? 0 - : CONTAINER_GRID_PADDING, - left: - differentParent && !isEmpty(dragCenterSpace) - ? dragCenterSpace.left * snapColumnSpace + - (noPad ? 0 : CONTAINER_GRID_PADDING) - : noPad - ? 0 - : CONTAINER_GRID_PADDING, - }; - let newRectanglesToDraw: { - top: number; - left: number; - width: number; - height: number; - columnWidth: number; - rowHeight: number; - widgetId: string; - isNotColliding: boolean; - }[] = []; - const canvasCtx: any = canvasRef.current.getContext("2d"); - canvasCtx.globalCompositeOperation = "destination-over"; - canvasCtx.scale(scale, scale); - - const startPoints = { - left: 20, - top: 20, - }; - const onMouseUp = () => { - if (isDragging && canvasIsDragging) { - onDrop(newRectanglesToDraw); - } - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } - startPoints.left = 20; - startPoints.top = 20; - const wasCanvasDragging = canvasIsDragging; - onMouseOut(); - if (wasCanvasDragging) { - if (newWidget) { - setDraggingNewWidget(false, undefined); - } else { - setDraggingState(false); - } - setDraggingCanvas(); - } - }; - - const onMouseDown = (e: any) => { - if ( - !isResizing && - isDragging && - !canvasIsDragging && - canvasRef.current - ) { - if (!isEmpty(dragCenterSpace)) { - startPoints.left = - ((dragParent === widgetId ? dragCenterSpace.left : 0) + - dragStartPoints.left) * - snapColumnSpace + - (noPad ? 0 : 2 * CONTAINER_GRID_PADDING); - startPoints.top = - ((dragParent === widgetId ? dragCenterSpace.top : 0) + - dragStartPoints.top) * - snapRowSpace + - (noPad ? 0 : 2 * CONTAINER_GRID_PADDING); - } - if (draggedOn !== widgetId) { - setDraggingCanvas(widgetId); - } - canvasIsDragging = true; - canvasRef.current.style.zIndex = "2"; - onMouseMove(e); - } - }; - const onMouseMove = (e: any) => { - if (isDragging && canvasIsDragging && canvasRef.current) { - const diff = { - left: e.offsetX - startPoints.left - parentDiff.left, - top: e.offsetY - startPoints.top - parentDiff.top, - }; - const currentOccSpaces = occupiedSpaces[widgetId]; - const occSpaces: OccupiedSpace[] = - dragParent === widgetId - ? childrenOccupiedSpaces.filter( - (each) => !selectedWidgets.includes(each.id), - ) - : currentOccSpaces; - const drawingBlocks = rectanglesToDraw.map((each) => ({ - ...each, - left: each.left + diff.left, - top: each.top + diff.top, - })); - const newRows = updateRows(drawingBlocks, rowRef.current); - const rowDiff = newRows ? newRows - rowRef.current : 0; - rowRef.current = newRows ? newRows : rowRef.current; - newRectanglesToDraw = drawingBlocks.map((each) => ({ - ...each, - isNotColliding: noCollision( - { x: each.left, y: each.top }, - snapColumnSpace, - snapRowSpace, - { x: 0, y: 0 }, - each.columnWidth, - each.rowHeight, - each.widgetId, - occSpaces, - rowRef.current, - GridDefaults.DEFAULT_GRID_COLUMNS, - ), - })); - if (rowDiff && canvasRef.current) { - notDoneYet = true; - drawInit(rowDiff, diff); - } else if (!notDoneYet) { - drawBlocks(); - } - checkIfNeedsScroll(e); - } else { - onMouseDown(e); - } - }; - const drawInit = debounce((rowDiff, diff) => { - notDoneYet = true; - if (canvasRef.current) { - newRectanglesToDraw = rectanglesToDraw.map((each) => { - return { - ...each, - left: each.left + diff.left, - top: each.top + diff.top, - }; - }); - canvasCtx.save(); - canvasRef.current.height = - (rowRef.current * snapRowSpace + (widgetId === "0" ? 200 : 0)) * - scale; - canvasCtx.scale(scale, scale); - canvasCtx.clearRect(0, 0, width, canvasRef.current.height); - canvasCtx.restore(); - drawBlocks(); - } - }); - - const drawBlocks = () => { - if (canvasRef.current && draggedOn === widgetId) { - const canvasCtx: any = canvasRef.current.getContext("2d"); - const { height, width } = canvasRef.current.getBoundingClientRect(); - canvasCtx.save(); - canvasCtx.clearRect(0, 0, width * scale, height * scale); - notDoneYet = false; - if (canvasIsDragging) { - newRectanglesToDraw.forEach((each) => { - drawRectangle(each); - }); - } - canvasCtx.restore(); - animationFrameId = window.requestAnimationFrame(drawBlocks); - } - }; - - const drawRectangle = (selectionDimensions: { - top: number; - left: number; - width: number; - height: number; - columnWidth: number; - rowHeight: number; - widgetId: string; - isNotColliding: boolean; - }) => { - if (canvasRef.current) { - const canvasCtx: any = canvasRef.current.getContext("2d"); - const snappedXY = getSnappedXY( - snapColumnSpace, - snapRowSpace, - { - x: selectionDimensions.left, - y: selectionDimensions.top, - }, - { - x: 0, - y: 0, - }, - ); - - canvasCtx.fillStyle = `${ - selectionDimensions.isNotColliding - ? "rgb(104, 113, 239, 0.6)" - : "red" - }`; - canvasCtx.fillRect( - selectionDimensions.left + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.top + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.width, - selectionDimensions.height, - ); - canvasCtx.fillStyle = `${ - selectionDimensions.isNotColliding - ? "rgb(233, 250, 243, 0.6)" - : "red" - }`; - const strokeWidth = 1; - canvasCtx.setLineDash([3]); - canvasCtx.strokeStyle = "rgb(104, 113, 239)"; - canvasCtx.strokeRect( - snappedXY.X + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), - snappedXY.Y + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.width - strokeWidth, - selectionDimensions.height - strokeWidth, - ); - } - }; - const startDragging = () => { - canvasRef.current?.addEventListener("mousemove", onMouseMove, false); - canvasRef.current?.addEventListener("mouseup", onMouseUp, false); - canvasRef.current?.addEventListener("mouseover", onMouseDown, false); - canvasRef.current?.addEventListener("mouseout", onMouseOut, false); - canvasRef.current?.addEventListener("mouseleave", onMouseOut, false); - document.body.addEventListener("mouseup", onMouseUp, false); - window.addEventListener("mouseup", onMouseUp, false); - - if (canvasIsDragging) { - rectanglesToDraw.forEach((each) => { - drawRectangle(each); - }); - } - }; - startDragging(); - if (dragParent === widgetId) { - canvasRef.current.style.zIndex = "2"; - } - return () => { - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } - window.cancelAnimationFrame(animationFrameId); - canvasRef.current?.removeEventListener("mousemove", onMouseMove); - canvasRef.current?.removeEventListener("mouseup", onMouseUp); - canvasRef.current?.removeEventListener("mouseover", onMouseDown); - canvasRef.current?.removeEventListener("mouseout", onMouseOut); - canvasRef.current?.removeEventListener("mouseleave", onMouseOut); - document.body.removeEventListener("mouseup", onMouseUp); - window.removeEventListener("mouseup", onMouseUp); - }; - } else { - onMouseOut(); - } - } - }, [isDragging, newWidget, isResizing, rectanglesToDraw, snapRows]); - - return isDragging && !isResizing ? ( + const { showCanvas } = useCanvasDragging(canvasRef, { + noPad, + snapColumnSpace, + snapRows, + snapRowSpace, + widgetId, + }); + + return showCanvas ? ( ) : null; } -CanvasDraggingArena.whyDidYouRender = { - logOnDifferentValues: true, -}; CanvasDraggingArena.displayName = "CanvasDraggingArena"; diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index 7271acbc0f2e..cc1e24e755a1 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -5,16 +5,12 @@ import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; const initialState: WidgetDragResizeState = { isDraggingDisabled: false, isDragging: false, + dragDetails: {}, isResizing: false, - dragGroupActualParent: "", - draggingGroupCenter: {}, - newWidget: undefined, lastSelectedWidget: undefined, selectedWidgets: [], focusedWidget: undefined, - selectedWidgetAncestory: [], - startPoints: undefined, - draggedOn: undefined, + selectedWidgetAncestry: [], }; export const widgetDraggingReducer = createImmerReducer(initialState, { @@ -30,7 +26,7 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { draggedOn: string; }>, ) => { - state.draggedOn = action.payload.draggedOn; + state.dragDetails.draggedOn = action.payload.draggedOn; }, [ReduxActionTypes.SET_WIDGET_DRAGGING]: ( state: WidgetDragResizeState, @@ -41,10 +37,12 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { startPoints: any; }>, ) => { - state.dragGroupActualParent = action.payload.dragGroupActualParent; state.isDragging = action.payload.isDragging; - state.draggingGroupCenter = action.payload.draggingGroupCenter; - state.startPoints = action.payload.startPoints; + state.dragDetails = { + dragGroupActualParent: action.payload.dragGroupActualParent, + draggingGroupCenter: action.payload.draggingGroupCenter, + dragOffset: action.payload.startPoints, + }; }, [ReduxActionTypes.SET_NEW_WIDGET_DRAGGING]: ( state: WidgetDragResizeState, @@ -54,7 +52,9 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { }>, ) => { state.isDragging = action.payload.isDragging; - state.newWidget = action.payload.newWidgetProps; + state.dragDetails = { + newWidget: action.payload.newWidgetProps, + }; }, [ReduxActionTypes.SET_WIDGET_RESIZING]: ( state: WidgetDragResizeState, @@ -133,7 +133,7 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { state: WidgetDragResizeState, action: ReduxAction, ) => { - state.selectedWidgetAncestory = action.payload; + state.selectedWidgetAncestry = action.payload; }, }); @@ -142,20 +142,23 @@ type DraggingGroupCenter = { top?: number; left?: number; }; +type DragDetails = { + dragGroupActualParent?: string; + draggingGroupCenter?: DraggingGroupCenter; + newWidget?: any; + draggedOn?: string; + dragOffset?: any; +}; export type WidgetDragResizeState = { isDraggingDisabled: boolean; isDragging: boolean; - dragGroupActualParent: string; - draggingGroupCenter: DraggingGroupCenter; + dragDetails: DragDetails; isResizing: boolean; lastSelectedWidget?: string; focusedWidget?: string; - selectedWidgetAncestory: string[]; + selectedWidgetAncestry: string[]; selectedWidgets: string[]; - newWidget: any; - startPoints: any; - draggedOn?: string; }; export default widgetDraggingReducer; diff --git a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts new file mode 100644 index 000000000000..7f29cb3083b1 --- /dev/null +++ b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -0,0 +1,242 @@ +import { useContext, useEffect, useRef } from "react"; +import { + CONTAINER_GRID_PADDING, + GridDefaults, + MAIN_CONTAINER_WIDGET_ID, +} from "constants/WidgetConstants"; +import { useSelector } from "store"; +import { AppState } from "reducers"; +import { getSelectedWidgets } from "selectors/ui"; +import { getOccupiedSpaces } from "selectors/editorSelectors"; +import { OccupiedSpace } from "constants/editorConstants"; +import { getWidgets } from "sagas/selectors"; +import { + getDropZoneOffsets, + widgetOperationParams, +} from "utils/WidgetPropsUtils"; +import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; +import { XYCoord } from "react-dnd"; +import { EditorContext } from "components/editorComponents/EditorContextProvider"; +import { isEmpty } from "lodash"; +import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena"; + +export type WidgetDraggingBlock = { + left: number; + top: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + isNotColliding: boolean; +}; + +export const useBlocksToBeDraggedOnCanvas = ({ + noPad, + snapColumnSpace, + snapRows, + snapRowSpace, + widgetId, +}: CanvasDraggingArenaProps) => { + const containerPadding = noPad ? 0 : CONTAINER_GRID_PADDING; + const dragDetails = useSelector( + (state: AppState) => state.ui.widgetDragResize.dragDetails, + ); + const defaultHandlePositions = { + top: 20, + left: 20, + }; + const { + draggingGroupCenter: dragCenter, + dragGroupActualParent: dragParent, + newWidget, + } = dragDetails; + const isResizing = useSelector( + (state: AppState) => state.ui.widgetDragResize.isResizing, + ); + const selectedWidgets = useSelector(getSelectedWidgets); + const occupiedSpaces = useSelector(getOccupiedSpaces) || {}; + const isNewWidget = !!newWidget && !dragParent; + const childrenOccupiedSpaces: OccupiedSpace[] = + (dragParent && occupiedSpaces[dragParent]) || []; + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); + + const allWidgets = useSelector(getWidgets); + const getDragCenterSpace = () => { + if (dragCenter && dragCenter.widgetId) { + // Dragging by widget + return ( + childrenOccupiedSpaces.find( + (each) => each.id === dragCenter.widgetId, + ) || {} + ); + } else if ( + dragCenter && + Number.isInteger(dragCenter.top) && + Number.isInteger(dragCenter.left) + ) { + // Dragging by Widget selection box + return dragCenter; + } else { + return {}; + } + }; + const getBlocksToDraw = (): WidgetDraggingBlock[] => { + if (isNewWidget) { + return [ + { + top: 0, + left: 0, + width: newWidget.columns * snapColumnSpace, + height: newWidget.rows * snapRowSpace, + columnWidth: newWidget.columns, + rowHeight: newWidget.rows, + widgetId: newWidget.widgetId, + isNotColliding: true, + }, + ]; + } else { + return childrenOccupiedSpaces + .filter((each) => selectedWidgets.includes(each.id)) + .map((each) => ({ + top: each.top * snapRowSpace + containerPadding, + left: each.left * snapColumnSpace + containerPadding, + width: (each.right - each.left) * snapColumnSpace, + height: (each.bottom - each.top) * snapRowSpace, + columnWidth: each.right - each.left, + rowHeight: each.bottom - each.top, + widgetId: each.id, + isNotColliding: true, + })); + } + }; + const blocksToDraw = getBlocksToDraw(); + const dragCenterSpace: any = getDragCenterSpace(); + + const filteredChildOccupiedSpaces = childrenOccupiedSpaces.filter( + (each) => !selectedWidgets.includes(each.id), + ); + const { persistDropTargetRows, updateDropTargetRows } = useContext( + DropTargetContext, + ); + const { updateWidget } = useContext(EditorContext); + + const onDrop = (drawingBlocks: WidgetDraggingBlock[]) => { + const cannotDrop = drawingBlocks.some((each) => { + return !each.isNotColliding; + }); + if (!cannotDrop) { + drawingBlocks + .sort( + (each1, each2) => + each1.top + each1.height - (each2.top + each2.height), + ) + .forEach((each) => { + const widget = newWidget ? newWidget : allWidgets[each.widgetId]; + const updateWidgetParams = widgetOperationParams( + widget, + { x: each.left, y: each.top }, + { x: 0, y: 0 }, + snapColumnSpace, + snapRowSpace, + widget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : widgetId, + ); + + const widgetBottomRow = + updateWidgetParams.payload.topRow + + (updateWidgetParams.payload.rows || + widget.bottomRow - widget.topRow); + persistDropTargetRows && + persistDropTargetRows(widget.widgetId, widgetBottomRow); + + /* Finally update the widget */ + updateWidget && + updateWidget( + updateWidgetParams.operation, + updateWidgetParams.widgetId, + updateWidgetParams.payload, + ); + }); + } + }; + const updateRows = (drawingBlocks: WidgetDraggingBlock[], rows: number) => { + if (drawingBlocks.length) { + const sortedByTopBlocks = drawingBlocks.sort( + (each1, each2) => each2.top + each2.height - (each1.top + each1.height), + ); + const bottomMostBlock = sortedByTopBlocks[0]; + const [, top] = getDropZoneOffsets( + snapColumnSpace, + snapRowSpace, + { + x: bottomMostBlock.left, + y: bottomMostBlock.top + bottomMostBlock.height, + } as XYCoord, + { x: 0, y: 0 }, + ); + if (top > rows - GridDefaults.CANVAS_EXTENSION_OFFSET) { + return updateDropTargetRows && updateDropTargetRows(widgetId, top); + } + } + }; + const rowRef = useRef(snapRows); + useEffect(() => { + rowRef.current = snapRows; + }, [snapRows, isDragging]); + + const isChildOfCanvas = dragParent === widgetId; + const parentDiff = isDragging + ? { + top: + !isChildOfCanvas && !isEmpty(dragCenterSpace) + ? dragCenterSpace.top * snapRowSpace + containerPadding + : containerPadding, + left: + !isChildOfCanvas && !isEmpty(dragCenterSpace) + ? dragCenterSpace.left * snapColumnSpace + containerPadding + : containerPadding, + } + : { + top: 0, + left: 0, + }; + + const relativeStartPoints = + isDragging && !isEmpty(dragCenterSpace) + ? { + left: + ((isChildOfCanvas ? dragCenterSpace.left : 0) + + dragDetails.dragOffset.left) * + snapColumnSpace + + 2 * containerPadding, + top: + ((isChildOfCanvas ? dragCenterSpace.top : 0) + + dragDetails.dragOffset.top) * + snapRowSpace + + 2 * containerPadding, + } + : defaultHandlePositions; + const currentOccSpaces = occupiedSpaces[widgetId]; + const occSpaces: OccupiedSpace[] = isChildOfCanvas + ? filteredChildOccupiedSpaces + : currentOccSpaces; + const isCurrentDraggedCanvas = dragDetails.draggedOn === widgetId; + return { + blocksToDraw, + dragCenterSpace, + dragDetails, + isChildOfCanvas, + isCurrentDraggedCanvas, + isDragging, + isNewWidget, + isResizing, + occSpaces, + onDrop, + parentDiff, + relativeStartPoints, + rowRef, + updateRows, + }; +}; diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts new file mode 100644 index 000000000000..0bd4afa995b2 --- /dev/null +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -0,0 +1,384 @@ +import { getSnappedXY } from "components/editorComponents/Dropzone"; +import { + CONTAINER_GRID_PADDING, + GridDefaults, +} from "constants/WidgetConstants"; +import { debounce } from "lodash"; +import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena"; +import { useEffect } from "react"; +import { getNearestParentCanvas } from "utils/generators"; +import { getScrollByPixels } from "utils/helpers"; +import { noCollision } from "utils/WidgetPropsUtils"; +import { useWidgetDragResize } from "./dragResizeHooks"; +import { + useBlocksToBeDraggedOnCanvas, + WidgetDraggingBlock, +} from "./useBlocksToBeDraggedOnCanvas"; + +export const useCanvasDragging = ( + canvasRef: React.RefObject, + { + noPad, + snapColumnSpace, + snapRows, + snapRowSpace, + widgetId, + }: CanvasDraggingArenaProps, +) => { + const { + blocksToDraw, + isChildOfCanvas, + isCurrentDraggedCanvas, + isDragging, + isNewWidget, + isResizing, + occSpaces, + onDrop, + parentDiff, + relativeStartPoints, + rowRef, + updateRows, + } = useBlocksToBeDraggedOnCanvas({ + noPad, + snapColumnSpace, + snapRows, + snapRowSpace, + widgetId, + }); + const { + setDraggingCanvas, + setDraggingNewWidget, + setDraggingState, + } = useWidgetDragResize(); + useEffect(() => { + if ( + canvasRef.current && + !isResizing && + isDragging && + blocksToDraw.length > 0 + ) { + const { devicePixelRatio: scale = 1 } = window; + + let canvasIsDragging = false; + let notDoneYet = false; + let animationFrameId: number; + let scrollTimeOut: number[] = []; + let scrollDirection = 0; + let scrollByPixels = 0; + let speed = 0; + const scrollFn = (scrollParent: Element | null) => { + if (isDragging && isCurrentDraggedCanvas && scrollParent) { + if ( + (scrollByPixels < 0 && scrollParent.scrollTop > 0) || + scrollByPixels > 0 + ) { + scrollParent.scrollBy({ + top: scrollByPixels, + behavior: "smooth", + }); + } + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } + scrollTimeOut.push( + setTimeout( + () => scrollFn(scrollParent), + 100 * Math.max(0.4, speed), + ), + ); + } else { + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } + } + }; + const checkIfNeedsScroll = debounce((e: any) => { + if (isDragging && canvasIsDragging && isCurrentDraggedCanvas) { + const scrollParent: Element | null = getNearestParentCanvas( + canvasRef.current, + ); + if (canvasRef.current && scrollParent) { + const scrollObj = getScrollByPixels( + { + top: e.offsetY, + height: 0, + }, + scrollParent, + canvasRef.current, + ); + scrollByPixels = scrollObj.scrollAmount; + speed = scrollObj.speed; + const currentScrollDirection = scrollByPixels + ? scrollByPixels > 0 + ? 1 + : -1 + : 0; + if (currentScrollDirection !== scrollDirection) { + scrollDirection = currentScrollDirection; + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } + if (!!scrollDirection) { + scrollFn(scrollParent); + } + } + } + } + }); + const onMouseOut = () => { + if (canvasRef.current) { + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } + const { height, width } = canvasRef.current.getBoundingClientRect(); + const canvasCtx: any = canvasRef.current.getContext("2d"); + canvasCtx.clearRect(0, 0, width * scale, height * scale); + canvasRef.current.style.zIndex = ""; + scrollDirection = 0; + canvasIsDragging = false; + } + }; + if (isDragging) { + const { height, width } = canvasRef.current.getBoundingClientRect(); + canvasRef.current.width = width * scale; + canvasRef.current.height = height * scale; + + let newRectanglesToDraw: { + top: number; + left: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + isNotColliding: boolean; + }[] = []; + const canvasCtx: any = canvasRef.current.getContext("2d"); + canvasCtx.globalCompositeOperation = "destination-over"; + canvasCtx.scale(scale, scale); + + const startPoints = { + left: 20, + top: 20, + }; + const onMouseUp = () => { + if (isDragging && canvasIsDragging) { + onDrop(newRectanglesToDraw); + } + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } + startPoints.left = 20; + startPoints.top = 20; + const wasCanvasDragging = canvasIsDragging; + onMouseOut(); + if (wasCanvasDragging) { + if (isNewWidget) { + setDraggingNewWidget(false, undefined); + } else { + setDraggingState(false); + } + setDraggingCanvas(); + } + }; + + const onMouseDown = (e: any) => { + if ( + !isResizing && + isDragging && + !canvasIsDragging && + canvasRef.current + ) { + if (!isNewWidget) { + startPoints.left = relativeStartPoints.left; + startPoints.top = relativeStartPoints.top; + } + if (!isCurrentDraggedCanvas) { + setDraggingCanvas(widgetId); + } + canvasIsDragging = true; + canvasRef.current.style.zIndex = "2"; + onMouseMove(e); + } + }; + const onMouseMove = (e: any) => { + if (isDragging && canvasIsDragging && canvasRef.current) { + const diff = { + left: e.offsetX - startPoints.left - parentDiff.left, + top: e.offsetY - startPoints.top - parentDiff.top, + }; + + const drawingBlocks = blocksToDraw.map((each) => ({ + ...each, + left: each.left + diff.left, + top: each.top + diff.top, + })); + const newRows = updateRows(drawingBlocks, rowRef.current); + const rowDiff = newRows ? newRows - rowRef.current : 0; + rowRef.current = newRows ? newRows : rowRef.current; + newRectanglesToDraw = drawingBlocks.map((each) => ({ + ...each, + isNotColliding: noCollision( + { x: each.left, y: each.top }, + snapColumnSpace, + snapRowSpace, + { x: 0, y: 0 }, + each.columnWidth, + each.rowHeight, + each.widgetId, + occSpaces, + rowRef.current, + GridDefaults.DEFAULT_GRID_COLUMNS, + ), + })); + if (rowDiff && canvasRef.current) { + notDoneYet = true; + drawInit(rowDiff, diff); + } else if (!notDoneYet) { + drawBlocks(); + } + checkIfNeedsScroll(e); + } else { + onMouseDown(e); + } + }; + const drawInit = debounce((rowDiff, diff) => { + notDoneYet = true; + if (canvasRef.current) { + newRectanglesToDraw = blocksToDraw.map((each) => { + return { + ...each, + left: each.left + diff.left, + top: each.top + diff.top, + }; + }); + canvasCtx.save(); + canvasRef.current.height = + (rowRef.current * snapRowSpace + (widgetId === "0" ? 200 : 0)) * + scale; + canvasCtx.scale(scale, scale); + canvasCtx.clearRect(0, 0, width, canvasRef.current.height); + canvasCtx.restore(); + drawBlocks(); + } + }); + + const drawBlocks = () => { + if (canvasRef.current && isCurrentDraggedCanvas) { + const canvasCtx: any = canvasRef.current.getContext("2d"); + const { height, width } = canvasRef.current.getBoundingClientRect(); + canvasCtx.save(); + canvasCtx.clearRect(0, 0, width * scale, height * scale); + notDoneYet = false; + if (canvasIsDragging) { + newRectanglesToDraw.forEach((each) => { + drawRectangle(each); + }); + } + canvasCtx.restore(); + animationFrameId = window.requestAnimationFrame(drawBlocks); + } + }; + + const drawRectangle = (selectionDimensions: WidgetDraggingBlock) => { + if (canvasRef.current) { + const canvasCtx: any = canvasRef.current.getContext("2d"); + const snappedXY = getSnappedXY( + snapColumnSpace, + snapRowSpace, + { + x: selectionDimensions.left, + y: selectionDimensions.top, + }, + { + x: 0, + y: 0, + }, + ); + + canvasCtx.fillStyle = `${ + selectionDimensions.isNotColliding + ? "rgb(104, 113, 239, 0.6)" + : "red" + }`; + canvasCtx.fillRect( + selectionDimensions.left + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.top + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.width, + selectionDimensions.height, + ); + canvasCtx.fillStyle = `${ + selectionDimensions.isNotColliding + ? "rgb(233, 250, 243, 0.6)" + : "red" + }`; + const strokeWidth = 1; + canvasCtx.setLineDash([3]); + canvasCtx.strokeStyle = "rgb(104, 113, 239)"; + canvasCtx.strokeRect( + snappedXY.X + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), + snappedXY.Y + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), + selectionDimensions.width - strokeWidth, + selectionDimensions.height - strokeWidth, + ); + } + }; + const startDragging = () => { + canvasRef.current?.addEventListener("mousemove", onMouseMove, false); + canvasRef.current?.addEventListener("mouseup", onMouseUp, false); + canvasRef.current?.addEventListener("mouseover", onMouseDown, false); + canvasRef.current?.addEventListener("mouseout", onMouseOut, false); + canvasRef.current?.addEventListener("mouseleave", onMouseOut, false); + document.body.addEventListener("mouseup", onMouseUp, false); + window.addEventListener("mouseup", onMouseUp, false); + + if (canvasIsDragging) { + blocksToDraw.forEach((each) => { + drawRectangle(each); + }); + } + }; + startDragging(); + if (isChildOfCanvas) { + canvasRef.current.style.zIndex = "2"; + } + return () => { + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } + window.cancelAnimationFrame(animationFrameId); + canvasRef.current?.removeEventListener("mousemove", onMouseMove); + canvasRef.current?.removeEventListener("mouseup", onMouseUp); + canvasRef.current?.removeEventListener("mouseover", onMouseDown); + canvasRef.current?.removeEventListener("mouseout", onMouseOut); + canvasRef.current?.removeEventListener("mouseleave", onMouseOut); + document.body.removeEventListener("mouseup", onMouseUp); + window.removeEventListener("mouseup", onMouseUp); + }; + } else { + onMouseOut(); + } + } + }, [isDragging, isResizing, blocksToDraw, snapRows]); + return { + showCanvas: isDragging && !isResizing, + }; +}; From 48767efc6220a3d7e821582c41e186bd48601f28 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 16 Jul 2021 09:38:59 +0530 Subject: [PATCH 30/68] dip --- .../src/pages/common/CanvasDraggingArena.tsx | 1 - .../hooks/useBlocksToBeDraggedOnCanvas.ts | 3 +- .../src/utils/hooks/useCanvasDragToScroll.ts | 98 +++++ .../src/utils/hooks/useCanvasDragging.ts | 341 ++++++++---------- 4 files changed, 259 insertions(+), 184 deletions(-) create mode 100644 app/client/src/utils/hooks/useCanvasDragToScroll.ts diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 1ea9ca91fc89..8f271400ab81 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -51,7 +51,6 @@ export function CanvasDraggingArena({ snapRowSpace, widgetId, }); - return showCanvas ? ( , + isCurrentDraggedCanvas: boolean, + isDragging: boolean, + snapRows: number, +) => { + const canScroll = useRef(true); + useEffect(() => { + if (isCurrentDraggedCanvas) { + let scrollTimeOut: number[] = []; + let scrollDirection = 0; + let scrollByPixels = 0; + let speed = 0; + const clearScrollStacks = () => { + if (scrollTimeOut.length) { + scrollTimeOut.forEach((each) => { + clearTimeout(each); + }); + scrollTimeOut = []; + } + }; + const scrollFn = (scrollParent: Element | null) => { + clearScrollStacks(); + if (!canScroll.current) { + scrollDirection = 0; + } + if ( + isDragging && + isCurrentDraggedCanvas && + scrollParent && + canScroll.current + ) { + if ( + (scrollByPixels < 0 && scrollParent.scrollTop > 0) || + scrollByPixels > 0 + ) { + scrollParent.scrollBy({ + top: scrollByPixels, + behavior: "smooth", + }); + } + scrollTimeOut.push( + setTimeout( + () => scrollFn(scrollParent), + 100 * Math.max(0.4, speed), + ), + ); + } + }; + const checkIfNeedsScroll = debounce((e: any) => { + if (isDragging && isCurrentDraggedCanvas) { + const scrollParent: Element | null = getNearestParentCanvas( + canvasRef.current, + ); + if (canvasRef.current && scrollParent) { + const scrollObj = getScrollByPixels( + { + top: e.offsetY, + height: 0, + }, + scrollParent, + canvasRef.current, + ); + scrollByPixels = scrollObj.scrollAmount; + speed = scrollObj.speed; + const currentScrollDirection = + canScroll.current && scrollByPixels + ? scrollByPixels > 0 + ? 1 + : -1 + : 0; + if (currentScrollDirection !== scrollDirection) { + scrollDirection = currentScrollDirection; + if (!!scrollDirection) { + scrollFn(scrollParent); + } + } + } + } + }); + canvasRef.current?.addEventListener( + "mousemove", + checkIfNeedsScroll, + false, + ); + return () => { + clearScrollStacks(); + canvasRef.current?.removeEventListener("mousemove", checkIfNeedsScroll); + }; + } + }, [isCurrentDraggedCanvas, snapRows]); + return canScroll; +}; diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index 0bd4afa995b2..1cac415da139 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -2,18 +2,19 @@ import { getSnappedXY } from "components/editorComponents/Dropzone"; import { CONTAINER_GRID_PADDING, GridDefaults, + MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; -import { debounce } from "lodash"; +import { debounce, defer, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena"; import { useEffect } from "react"; import { getNearestParentCanvas } from "utils/generators"; -import { getScrollByPixels } from "utils/helpers"; import { noCollision } from "utils/WidgetPropsUtils"; import { useWidgetDragResize } from "./dragResizeHooks"; import { useBlocksToBeDraggedOnCanvas, WidgetDraggingBlock, } from "./useBlocksToBeDraggedOnCanvas"; +import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; export const useCanvasDragging = ( canvasRef: React.RefObject, @@ -27,6 +28,7 @@ export const useCanvasDragging = ( ) => { const { blocksToDraw, + defaultHandlePositions, isChildOfCanvas, isCurrentDraggedCanvas, isDragging, @@ -50,6 +52,15 @@ export const useCanvasDragging = ( setDraggingNewWidget, setDraggingState, } = useWidgetDragResize(); + const scrollParent: Element | null = getNearestParentCanvas( + canvasRef.current, + ); + const canScroll = useCanvasDragToScroll( + canvasRef, + isCurrentDraggedCanvas, + isDragging, + snapRows, + ); useEffect(() => { if ( canvasRef.current && @@ -60,134 +71,30 @@ export const useCanvasDragging = ( const { devicePixelRatio: scale = 1 } = window; let canvasIsDragging = false; - let notDoneYet = false; + let isUpdatingRows = false; let animationFrameId: number; - let scrollTimeOut: number[] = []; - let scrollDirection = 0; - let scrollByPixels = 0; - let speed = 0; - const scrollFn = (scrollParent: Element | null) => { - if (isDragging && isCurrentDraggedCanvas && scrollParent) { - if ( - (scrollByPixels < 0 && scrollParent.scrollTop > 0) || - scrollByPixels > 0 - ) { - scrollParent.scrollBy({ - top: scrollByPixels, - behavior: "smooth", - }); - } - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } - scrollTimeOut.push( - setTimeout( - () => scrollFn(scrollParent), - 100 * Math.max(0.4, speed), - ), - ); - } else { - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } - } - }; - const checkIfNeedsScroll = debounce((e: any) => { - if (isDragging && canvasIsDragging && isCurrentDraggedCanvas) { - const scrollParent: Element | null = getNearestParentCanvas( - canvasRef.current, - ); - if (canvasRef.current && scrollParent) { - const scrollObj = getScrollByPixels( - { - top: e.offsetY, - height: 0, - }, - scrollParent, - canvasRef.current, - ); - scrollByPixels = scrollObj.scrollAmount; - speed = scrollObj.speed; - const currentScrollDirection = scrollByPixels - ? scrollByPixels > 0 - ? 1 - : -1 - : 0; - if (currentScrollDirection !== scrollDirection) { - scrollDirection = currentScrollDirection; - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } - if (!!scrollDirection) { - scrollFn(scrollParent); - } - } - } - } - }); - const onMouseOut = () => { + let currentRectanglesToDraw: WidgetDraggingBlock[] = []; + const scrollObj: any = {}; + + const resetCanvasState = () => { if (canvasRef.current) { - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } const { height, width } = canvasRef.current.getBoundingClientRect(); const canvasCtx: any = canvasRef.current.getContext("2d"); canvasCtx.clearRect(0, 0, width * scale, height * scale); canvasRef.current.style.zIndex = ""; - scrollDirection = 0; canvasIsDragging = false; } }; if (isDragging) { - const { height, width } = canvasRef.current.getBoundingClientRect(); - canvasRef.current.width = width * scale; - canvasRef.current.height = height * scale; - - let newRectanglesToDraw: { - top: number; - left: number; - width: number; - height: number; - columnWidth: number; - rowHeight: number; - widgetId: string; - isNotColliding: boolean; - }[] = []; - const canvasCtx: any = canvasRef.current.getContext("2d"); - canvasCtx.globalCompositeOperation = "destination-over"; - canvasCtx.scale(scale, scale); - - const startPoints = { - left: 20, - top: 20, - }; + const startPoints = defaultHandlePositions; const onMouseUp = () => { if (isDragging && canvasIsDragging) { - onDrop(newRectanglesToDraw); + onDrop(currentRectanglesToDraw); } - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } - startPoints.left = 20; - startPoints.top = 20; - const wasCanvasDragging = canvasIsDragging; - onMouseOut(); - if (wasCanvasDragging) { + startPoints.top = defaultHandlePositions.top; + startPoints.left = defaultHandlePositions.left; + resetCanvasState(); + if (isCurrentDraggedCanvas) { if (isNewWidget) { setDraggingNewWidget(false, undefined); } else { @@ -197,7 +104,7 @@ export const useCanvasDragging = ( } }; - const onMouseDown = (e: any) => { + const onFirstMoveOnCanvas = (e: any) => { if ( !isResizing && isDragging && @@ -209,6 +116,7 @@ export const useCanvasDragging = ( startPoints.top = relativeStartPoints.top; } if (!isCurrentDraggedCanvas) { + // we can just use canvasIsDragging but this is needed to render the relative DragLayerComponent setDraggingCanvas(widgetId); } canvasIsDragging = true; @@ -216,22 +124,22 @@ export const useCanvasDragging = ( onMouseMove(e); } }; - const onMouseMove = (e: any) => { + const onMouseMove = (e: any, scroll = false) => { if (isDragging && canvasIsDragging && canvasRef.current) { - const diff = { + const delta = { left: e.offsetX - startPoints.left - parentDiff.left, top: e.offsetY - startPoints.top - parentDiff.top, }; const drawingBlocks = blocksToDraw.map((each) => ({ ...each, - left: each.left + diff.left, - top: each.top + diff.top, + left: each.left + delta.left, + top: each.top + delta.top, })); const newRows = updateRows(drawingBlocks, rowRef.current); - const rowDiff = newRows ? newRows - rowRef.current : 0; + const rowDelta = newRows ? newRows - rowRef.current : 0; rowRef.current = newRows ? newRows : rowRef.current; - newRectanglesToDraw = drawingBlocks.map((each) => ({ + currentRectanglesToDraw = drawingBlocks.map((each) => ({ ...each, isNotColliding: noCollision( { x: each.left, y: each.top }, @@ -246,64 +154,89 @@ export const useCanvasDragging = ( GridDefaults.DEFAULT_GRID_COLUMNS, ), })); - if (rowDiff && canvasRef.current) { - notDoneYet = true; - drawInit(rowDiff, diff); - } else if (!notDoneYet) { - drawBlocks(); + if (rowDelta && canvasRef.current) { + isUpdatingRows = true; + renderNewRows(delta); + canScroll.current = false; + } else if (!isUpdatingRows) { + renderBlocks(); } - checkIfNeedsScroll(e); + scrollObj.lastMouseMoveEvent = e; + scrollObj.lastScrollTop = scrollParent?.scrollTop; + scrollObj.lastScrollHeight = scrollParent?.scrollHeight; } else { - onMouseDown(e); + onFirstMoveOnCanvas(e); } }; - const drawInit = debounce((rowDiff, diff) => { - notDoneYet = true; + const renderNewRows = debounce((delta) => { + isUpdatingRows = true; if (canvasRef.current) { - newRectanglesToDraw = blocksToDraw.map((each) => { + const canvasCtx: any = canvasRef.current.getContext("2d"); + + currentRectanglesToDraw = blocksToDraw.map((each) => { return { ...each, - left: each.left + diff.left, - top: each.top + diff.top, + left: each.left + delta.left, + top: each.top + delta.top, }; }); canvasCtx.save(); canvasRef.current.height = - (rowRef.current * snapRowSpace + (widgetId === "0" ? 200 : 0)) * + (rowRef.current * snapRowSpace + + (widgetId === MAIN_CONTAINER_WIDGET_ID ? 200 : 0)) * scale; canvasCtx.scale(scale, scale); - canvasCtx.clearRect(0, 0, width, canvasRef.current.height); + canvasCtx.clearRect( + 0, + 0, + canvasRef.current.width, + canvasRef.current.height, + ); canvasCtx.restore(); - drawBlocks(); + renderBlocks(); + endRenderRows(); } }); - const drawBlocks = () => { + const endRenderRows = throttle( + () => { + canScroll.current = true; + scrollObj.lastScrollHeight = scrollParent?.scrollHeight; + scrollObj.lastScrollTop = scrollParent?.scrollTop; + }, + 100, + { + leading: true, + trailing: true, + }, + ); + + const renderBlocks = () => { if (canvasRef.current && isCurrentDraggedCanvas) { const canvasCtx: any = canvasRef.current.getContext("2d"); const { height, width } = canvasRef.current.getBoundingClientRect(); canvasCtx.save(); canvasCtx.clearRect(0, 0, width * scale, height * scale); - notDoneYet = false; + isUpdatingRows = false; if (canvasIsDragging) { - newRectanglesToDraw.forEach((each) => { - drawRectangle(each); + currentRectanglesToDraw.forEach((each) => { + drawBlockOnCanvas(each); }); } canvasCtx.restore(); - animationFrameId = window.requestAnimationFrame(drawBlocks); + animationFrameId = window.requestAnimationFrame(renderBlocks); } }; - const drawRectangle = (selectionDimensions: WidgetDraggingBlock) => { + const drawBlockOnCanvas = (blockDimensions: WidgetDraggingBlock) => { if (canvasRef.current) { const canvasCtx: any = canvasRef.current.getContext("2d"); const snappedXY = getSnappedXY( snapColumnSpace, snapRowSpace, { - x: selectionDimensions.left, - y: selectionDimensions.top, + x: blockDimensions.left, + y: blockDimensions.top, }, { x: 0, @@ -312,20 +245,16 @@ export const useCanvasDragging = ( ); canvasCtx.fillStyle = `${ - selectionDimensions.isNotColliding - ? "rgb(104, 113, 239, 0.6)" - : "red" + blockDimensions.isNotColliding ? "rgb(104, 113, 239, 0.6)" : "red" }`; canvasCtx.fillRect( - selectionDimensions.left + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.top + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.width, - selectionDimensions.height, + blockDimensions.left + (noPad ? 0 : CONTAINER_GRID_PADDING), + blockDimensions.top + (noPad ? 0 : CONTAINER_GRID_PADDING), + blockDimensions.width, + blockDimensions.height, ); canvasCtx.fillStyle = `${ - selectionDimensions.isNotColliding - ? "rgb(233, 250, 243, 0.6)" - : "red" + blockDimensions.isNotColliding ? "rgb(233, 250, 243, 0.6)" : "red" }`; const strokeWidth = 1; canvasCtx.setLineDash([3]); @@ -333,48 +262,98 @@ export const useCanvasDragging = ( canvasCtx.strokeRect( snappedXY.X + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), snappedXY.Y + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), - selectionDimensions.width - strokeWidth, - selectionDimensions.height - strokeWidth, + blockDimensions.width - strokeWidth, + blockDimensions.height - strokeWidth, ); } }; - const startDragging = () => { + const onScroll = () => { + const { + lastMouseMoveEvent, + lastScrollHeight, + lastScrollTop, + } = scrollObj; + if ( + lastMouseMoveEvent && + Number.isInteger(lastScrollHeight) && + Number.isInteger(lastScrollTop) && + scrollParent && + canScroll.current + ) { + const delta = + scrollParent?.scrollHeight + + scrollParent?.scrollTop - + (lastScrollHeight + lastScrollTop); + onMouseMove( + { + offsetX: lastMouseMoveEvent.offsetX, + offsetY: lastMouseMoveEvent.offsetY + delta, + }, + true, + ); + } + }; + const initializeListeners = () => { canvasRef.current?.addEventListener("mousemove", onMouseMove, false); canvasRef.current?.addEventListener("mouseup", onMouseUp, false); - canvasRef.current?.addEventListener("mouseover", onMouseDown, false); - canvasRef.current?.addEventListener("mouseout", onMouseOut, false); - canvasRef.current?.addEventListener("mouseleave", onMouseOut, false); + scrollParent?.addEventListener("scroll", onScroll, false); + canvasRef.current?.addEventListener( + "mouseover", + onFirstMoveOnCanvas, + false, + ); + canvasRef.current?.addEventListener( + "mouseout", + resetCanvasState, + false, + ); + canvasRef.current?.addEventListener( + "mouseleave", + resetCanvasState, + false, + ); document.body.addEventListener("mouseup", onMouseUp, false); window.addEventListener("mouseup", onMouseUp, false); - - if (canvasIsDragging) { - blocksToDraw.forEach((each) => { - drawRectangle(each); - }); + }; + const startDragging = () => { + if (canvasRef.current) { + const { height, width } = canvasRef.current.getBoundingClientRect(); + canvasRef.current.width = width * scale; + canvasRef.current.height = height * scale; + const canvasCtx: any = canvasRef.current.getContext("2d"); + canvasCtx.scale(scale, scale); + initializeListeners(); + if (canvasIsDragging) { + blocksToDraw.forEach((each) => { + drawBlockOnCanvas(each); + }); + } + if (isChildOfCanvas && canvasRef.current) { + canvasRef.current.style.zIndex = "2"; + } } }; startDragging(); - if (isChildOfCanvas) { - canvasRef.current.style.zIndex = "2"; - } + return () => { - if (scrollTimeOut.length) { - scrollTimeOut.forEach((each) => { - clearTimeout(each); - }); - scrollTimeOut = []; - } window.cancelAnimationFrame(animationFrameId); canvasRef.current?.removeEventListener("mousemove", onMouseMove); canvasRef.current?.removeEventListener("mouseup", onMouseUp); - canvasRef.current?.removeEventListener("mouseover", onMouseDown); - canvasRef.current?.removeEventListener("mouseout", onMouseOut); - canvasRef.current?.removeEventListener("mouseleave", onMouseOut); + scrollParent?.removeEventListener("scroll", onScroll); + canvasRef.current?.removeEventListener( + "mouseover", + onFirstMoveOnCanvas, + ); + canvasRef.current?.removeEventListener("mouseout", resetCanvasState); + canvasRef.current?.removeEventListener( + "mouseleave", + resetCanvasState, + ); document.body.removeEventListener("mouseup", onMouseUp); window.removeEventListener("mouseup", onMouseUp); }; } else { - onMouseOut(); + resetCanvasState(); } } }, [isDragging, isResizing, blocksToDraw, snapRows]); From 4edc163f2b532bf9f53ba3716be1bbcfe67f1933 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 16 Jul 2021 11:31:00 +0530 Subject: [PATCH 31/68] dip --- .../src/utils/hooks/useCanvasDragging.ts | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index 1cac415da139..11ca7c92ad39 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -4,7 +4,7 @@ import { GridDefaults, MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; -import { debounce, defer, throttle } from "lodash"; +import { debounce, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena"; import { useEffect } from "react"; import { getNearestParentCanvas } from "utils/generators"; @@ -124,7 +124,7 @@ export const useCanvasDragging = ( onMouseMove(e); } }; - const onMouseMove = (e: any, scroll = false) => { + const onMouseMove = (e: any) => { if (isDragging && canvasIsDragging && canvasRef.current) { const delta = { left: e.offsetX - startPoints.left - parentDiff.left, @@ -157,7 +157,6 @@ export const useCanvasDragging = ( if (rowDelta && canvasRef.current) { isUpdatingRows = true; renderNewRows(delta); - canScroll.current = false; } else if (!isUpdatingRows) { renderBlocks(); } @@ -277,26 +276,22 @@ export const useCanvasDragging = ( lastMouseMoveEvent && Number.isInteger(lastScrollHeight) && Number.isInteger(lastScrollTop) && - scrollParent && - canScroll.current + scrollParent ) { const delta = scrollParent?.scrollHeight + scrollParent?.scrollTop - (lastScrollHeight + lastScrollTop); - onMouseMove( - { - offsetX: lastMouseMoveEvent.offsetX, - offsetY: lastMouseMoveEvent.offsetY + delta, - }, - true, - ); + onMouseMove({ + offsetX: lastMouseMoveEvent.offsetX, + offsetY: lastMouseMoveEvent.offsetY + delta, + }); } }; const initializeListeners = () => { canvasRef.current?.addEventListener("mousemove", onMouseMove, false); canvasRef.current?.addEventListener("mouseup", onMouseUp, false); - scrollParent?.addEventListener("scroll", onScroll, false); + // scrollParent?.addEventListener("scroll", onScroll, false); canvasRef.current?.addEventListener( "mouseover", onFirstMoveOnCanvas, From 641f47e4ac0b69e4a7c9a902cd7dd1c3790291b4 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 16 Jul 2021 22:24:30 +0530 Subject: [PATCH 32/68] ---dip --- .../src/pages/common/CanvasDraggingArena.tsx | 28 +++- .../src/pages/common/CanvasSelectionArena.tsx | 24 ++- app/client/src/utils/helpers.tsx | 1 - .../src/utils/hooks/useCanvasDragToScroll.ts | 17 +- .../src/utils/hooks/useCanvasDragging.ts | 156 ++++++++++++++---- app/client/src/widgets/ContainerWidget.tsx | 4 +- 6 files changed, 167 insertions(+), 63 deletions(-) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index 8f271400ab81..aff725064201 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -3,8 +3,10 @@ import React, { useMemo } from "react"; import styled from "styled-components"; import { theme } from "constants/DefaultTheme"; import { useCanvasDragging } from "utils/hooks/useCanvasDragging"; +import { useEffect } from "react"; +import { getNearestParentCanvas } from "utils/generators"; -const StyledSelectionCanvas = styled.canvas<{ paddingBottom: number }>` +const StyledSelectionCanvas = styled.div<{ paddingBottom: number }>` position: absolute; top: 0px; left: 0px; @@ -43,21 +45,29 @@ export function CanvasDraggingArena({ return widgetId === MAIN_CONTAINER_WIDGET_ID; }, [widgetId]); - const canvasRef = React.useRef(null); - const { showCanvas } = useCanvasDragging(canvasRef, { + const canvasRef = React.useRef(null); + const canvasDrawRef = React.useRef(null); + const { showCanvas } = useCanvasDragging(canvasRef, canvasDrawRef, { noPad, snapColumnSpace, snapRows, snapRowSpace, widgetId, }); + return showCanvas ? ( - + <> + + + ) : null; } CanvasDraggingArena.displayName = "CanvasDraggingArena"; diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 1b17f75b791b..6adf95b6e128 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -14,6 +14,7 @@ import { getCurrentPageId, } from "selectors/editorSelectors"; import styled from "styled-components"; +import { getNearestParentCanvas } from "utils/generators"; const StyledSelectionCanvas = styled.canvas` position: absolute; @@ -54,6 +55,9 @@ export function CanvasSelectionArena({ const mainContainer = useSelector((state: AppState) => getWidget(state, widgetId), ); + const scrollParent: Element | null = getNearestParentCanvas( + canvasRef.current, + ); const currentPageId = useSelector(getCurrentPageId); const appLayout = useSelector(getCurrentApplicationLayout); const throttledWidgetSelection = useCallback( @@ -83,7 +87,8 @@ export function CanvasSelectionArena({ ); useEffect(() => { if (appMode === APP_MODE.EDIT && !isDragging && canvasRef.current) { - const { devicePixelRatio: scale = 1 } = window; + // const { devicePixelRatio: scale = 1 } = window; + const scale = 1; let canvasCtx: any = canvasRef.current.getContext("2d"); const initRectangle = (): SelectedArenaDimensions => ({ @@ -221,6 +226,9 @@ export function CanvasSelectionArena({ } }; const onMouseMove = (e: any) => { + const { height = 0, width = 0 } = + scrollParent?.getBoundingClientRect() || {}; + if (isDragging && canvasRef.current) { selectionRectangle.width = e.offsetX - canvasRef.current.offsetLeft - selectionRectangle.left; @@ -257,13 +265,17 @@ export function CanvasSelectionArena({ snapRows, // mainContainer.minHeight, ]); + const { height = 0, width = 0 } = scrollParent?.getBoundingClientRect() || {}; return appMode === APP_MODE.EDIT && !isDragging ? ( - + <> + + {/* */} + ) : null; } CanvasSelectionArena.displayName = "CanvasSelectionArena"; diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index 0bc4c6714619..01c1cd51aab3 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -64,7 +64,6 @@ export const getScrollByPixels = function( scrollAmount: number; speed: number; } { - // const bounding = elem.getBoundingClientRect(); const scrollParentBounds = scrollParent.getBoundingClientRect(); const scrollChildBounds = child.getBoundingClientRect(); const scrollAmount = diff --git a/app/client/src/utils/hooks/useCanvasDragToScroll.ts b/app/client/src/utils/hooks/useCanvasDragToScroll.ts index c368a19a2522..ddd046726476 100644 --- a/app/client/src/utils/hooks/useCanvasDragToScroll.ts +++ b/app/client/src/utils/hooks/useCanvasDragToScroll.ts @@ -4,7 +4,7 @@ import { getNearestParentCanvas } from "utils/generators"; import { getScrollByPixels } from "utils/helpers"; export const useCanvasDragToScroll = ( - canvasRef: RefObject, + canvasRef: RefObject, isCurrentDraggedCanvas: boolean, isDragging: boolean, snapRows: number, @@ -24,11 +24,14 @@ export const useCanvasDragToScroll = ( scrollTimeOut = []; } }; - const scrollFn = (scrollParent: Element | null) => { + const scrollFn = () => { clearScrollStacks(); if (!canScroll.current) { scrollDirection = 0; } + const scrollParent: Element | null = getNearestParentCanvas( + canvasRef.current, + ); if ( isDragging && isCurrentDraggedCanvas && @@ -39,17 +42,13 @@ export const useCanvasDragToScroll = ( (scrollByPixels < 0 && scrollParent.scrollTop > 0) || scrollByPixels > 0 ) { + console.count("scrollFn"); scrollParent.scrollBy({ top: scrollByPixels, behavior: "smooth", }); } - scrollTimeOut.push( - setTimeout( - () => scrollFn(scrollParent), - 100 * Math.max(0.4, speed), - ), - ); + scrollTimeOut.push(setTimeout(scrollFn, 100 * Math.max(0.4, speed))); } }; const checkIfNeedsScroll = debounce((e: any) => { @@ -77,7 +76,7 @@ export const useCanvasDragToScroll = ( if (currentScrollDirection !== scrollDirection) { scrollDirection = currentScrollDirection; if (!!scrollDirection) { - scrollFn(scrollParent); + scrollFn(); } } } diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index 11ca7c92ad39..a3d5f48d84b1 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -7,6 +7,7 @@ import { import { debounce, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena"; import { useEffect } from "react"; +import { height } from "styled-system"; import { getNearestParentCanvas } from "utils/generators"; import { noCollision } from "utils/WidgetPropsUtils"; import { useWidgetDragResize } from "./dragResizeHooks"; @@ -17,7 +18,8 @@ import { import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; export const useCanvasDragging = ( - canvasRef: React.RefObject, + canvasRef: React.RefObject, + canvasDrawRef: React.RefObject, { noPad, snapColumnSpace, @@ -52,9 +54,22 @@ export const useCanvasDragging = ( setDraggingNewWidget, setDraggingState, } = useWidgetDragResize(); - const scrollParent: Element | null = getNearestParentCanvas( - canvasRef.current, - ); + const updateCanvasPosition = () => { + const scrollParent: Element | null = getNearestParentCanvas( + canvasRef.current, + ); + if (scrollParent && canvasDrawRef.current) { + const { devicePixelRatio: scale = 1 } = window; + const { height, width } = scrollParent.getBoundingClientRect(); + canvasDrawRef.current.style.width = width + "px"; + canvasDrawRef.current.style.height = height + "px"; + canvasDrawRef.current.width = width * scale; + canvasDrawRef.current.height = height * scale; + canvasDrawRef.current.style.top = scrollParent.scrollTop + "px"; + const canvasCtx: any = canvasDrawRef.current.getContext("2d"); + canvasCtx.scale(scale, scale); + } + }; const canScroll = useCanvasDragToScroll( canvasRef, isCurrentDraggedCanvas, @@ -68,8 +83,11 @@ export const useCanvasDragging = ( isDragging && blocksToDraw.length > 0 ) { + const scrollParent: Element | null = getNearestParentCanvas( + canvasRef.current, + ); const { devicePixelRatio: scale = 1 } = window; - + // const scale = 1; let canvasIsDragging = false; let isUpdatingRows = false; let animationFrameId: number; @@ -77,10 +95,14 @@ export const useCanvasDragging = ( const scrollObj: any = {}; const resetCanvasState = () => { - if (canvasRef.current) { - const { height, width } = canvasRef.current.getBoundingClientRect(); - const canvasCtx: any = canvasRef.current.getContext("2d"); - canvasCtx.clearRect(0, 0, width * scale, height * scale); + if (canvasDrawRef.current && canvasRef.current) { + const canvasCtx: any = canvasDrawRef.current.getContext("2d"); + canvasCtx.clearRect( + 0, + 0, + canvasDrawRef.current.width, + canvasDrawRef.current.height, + ); canvasRef.current.style.zIndex = ""; canvasIsDragging = false; } @@ -156,6 +178,7 @@ export const useCanvasDragging = ( })); if (rowDelta && canvasRef.current) { isUpdatingRows = true; + canScroll.current = false; renderNewRows(delta); } else if (!isUpdatingRows) { renderBlocks(); @@ -169,8 +192,8 @@ export const useCanvasDragging = ( }; const renderNewRows = debounce((delta) => { isUpdatingRows = true; - if (canvasRef.current) { - const canvasCtx: any = canvasRef.current.getContext("2d"); + if (canvasRef.current && canvasDrawRef.current) { + const canvasCtx: any = canvasDrawRef.current.getContext("2d"); currentRectanglesToDraw = blocksToDraw.map((each) => { return { @@ -180,42 +203,85 @@ export const useCanvasDragging = ( }; }); canvasCtx.save(); - canvasRef.current.height = - (rowRef.current * snapRowSpace + - (widgetId === MAIN_CONTAINER_WIDGET_ID ? 200 : 0)) * - scale; + // canvasRef.current.style.height = + // (rowRef.current * snapRowSpace + + // (widgetId === MAIN_CONTAINER_WIDGET_ID ? 200 : 0)) * + // scale; canvasCtx.scale(scale, scale); canvasCtx.clearRect( 0, 0, - canvasRef.current.width, - canvasRef.current.height, + canvasDrawRef.current.width, + canvasDrawRef.current.height, ); canvasCtx.restore(); renderBlocks(); + canScroll.current = false; + endRenderRows.cancel(); + console.count("endRenderRows"); endRenderRows(); } }); + const debouncedFn = debounce( + () => { + console.count("debouncedFn"); + if (scrollParent) { + const { + lastMouseMoveEvent, + lastScrollHeight, + lastScrollTop, + } = scrollObj; + const delta = + scrollParent?.scrollHeight + + scrollParent?.scrollTop - + (lastScrollHeight + lastScrollTop); + if (delta) { + scrollParent.scrollBy({ top: delta, behavior: "smooth" }); + } + onMouseMove({ + offsetX: lastMouseMoveEvent.offsetX, + offsetY: lastMouseMoveEvent.offsetY + delta, + }); + canScroll.current = true; + } + }, + 50, + { + leading: false, + trailing: true, + }, + ); + const endRenderRows = throttle( () => { + canScroll.current = false; + debouncedFn.cancel(); canScroll.current = true; - scrollObj.lastScrollHeight = scrollParent?.scrollHeight; - scrollObj.lastScrollTop = scrollParent?.scrollTop; + + // debouncedFn(); }, - 100, + 50, { - leading: true, + leading: false, trailing: true, }, ); const renderBlocks = () => { - if (canvasRef.current && isCurrentDraggedCanvas) { - const canvasCtx: any = canvasRef.current.getContext("2d"); - const { height, width } = canvasRef.current.getBoundingClientRect(); + if ( + canvasRef.current && + isCurrentDraggedCanvas && + canvasDrawRef.current + ) { + const canvasCtx: any = canvasDrawRef.current.getContext("2d"); canvasCtx.save(); - canvasCtx.clearRect(0, 0, width * scale, height * scale); + canvasCtx.clearRect( + 0, + 0, + canvasDrawRef.current.width, + canvasDrawRef.current.height, + ); isUpdatingRows = false; if (canvasIsDragging) { currentRectanglesToDraw.forEach((each) => { @@ -228,14 +294,26 @@ export const useCanvasDragging = ( }; const drawBlockOnCanvas = (blockDimensions: WidgetDraggingBlock) => { - if (canvasRef.current) { - const canvasCtx: any = canvasRef.current.getContext("2d"); + if (canvasDrawRef.current && canvasRef.current && scrollParent) { + const canvasCtx: any = canvasDrawRef.current.getContext("2d"); + const { top } = canvasRef.current.getBoundingClientRect(); + const { + height: parentHeight, + top: parentTop, + } = scrollParent.getBoundingClientRect(); + const offSetCozOfPadding = + parentHeight + parentTop + scrollParent.scrollTop > + scrollParent.scrollHeight + ? scrollParent.scrollHeight - + (parentHeight + parentTop + scrollParent.scrollTop) + : 0; + const topOffset = scrollParent.scrollTop; const snappedXY = getSnappedXY( snapColumnSpace, snapRowSpace, { x: blockDimensions.left, - y: blockDimensions.top, + y: blockDimensions.top - topOffset, }, { x: 0, @@ -248,7 +326,9 @@ export const useCanvasDragging = ( }`; canvasCtx.fillRect( blockDimensions.left + (noPad ? 0 : CONTAINER_GRID_PADDING), - blockDimensions.top + (noPad ? 0 : CONTAINER_GRID_PADDING), + blockDimensions.top - + topOffset + + (noPad ? 0 : CONTAINER_GRID_PADDING), blockDimensions.width, blockDimensions.height, ); @@ -276,12 +356,16 @@ export const useCanvasDragging = ( lastMouseMoveEvent && Number.isInteger(lastScrollHeight) && Number.isInteger(lastScrollTop) && - scrollParent + scrollParent && + canScroll.current ) { const delta = scrollParent?.scrollHeight + scrollParent?.scrollTop - (lastScrollHeight + lastScrollTop); + if (delta) { + console.count("onScroll"); + } onMouseMove({ offsetX: lastMouseMoveEvent.offsetX, offsetY: lastMouseMoveEvent.offsetY + delta, @@ -291,7 +375,9 @@ export const useCanvasDragging = ( const initializeListeners = () => { canvasRef.current?.addEventListener("mousemove", onMouseMove, false); canvasRef.current?.addEventListener("mouseup", onMouseUp, false); - // scrollParent?.addEventListener("scroll", onScroll, false); + scrollParent?.addEventListener("scroll", updateCanvasPosition, false); + scrollParent?.addEventListener("scroll", onScroll, false); + canvasRef.current?.addEventListener( "mouseover", onFirstMoveOnCanvas, @@ -311,12 +397,10 @@ export const useCanvasDragging = ( window.addEventListener("mouseup", onMouseUp, false); }; const startDragging = () => { - if (canvasRef.current) { - const { height, width } = canvasRef.current.getBoundingClientRect(); - canvasRef.current.width = width * scale; - canvasRef.current.height = height * scale; - const canvasCtx: any = canvasRef.current.getContext("2d"); + if (canvasRef.current && canvasDrawRef.current) { + const canvasCtx: any = canvasDrawRef.current.getContext("2d"); canvasCtx.scale(scale, scale); + updateCanvasPosition(); initializeListeners(); if (canvasIsDragging) { blocksToDraw.forEach((each) => { diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index 8a108f9e5b91..0c7393fc91c3 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -135,11 +135,11 @@ class ContainerWidget extends BaseWidget< snapRows={snapRows} widgetId={props.widgetId} /> - + /> */} )} Date: Sat, 17 Jul 2021 08:37:33 +0530 Subject: [PATCH 33/68] dip --- .../src/pages/common/CanvasDraggingArena.tsx | 14 ++-- .../src/pages/common/CanvasSelectionArena.tsx | 8 --- app/client/src/utils/generators.tsx | 7 ++ .../src/utils/hooks/useCanvasDragging.ts | 66 +++++++++---------- app/client/src/widgets/ContainerWidget.tsx | 5 +- 5 files changed, 47 insertions(+), 53 deletions(-) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index aff725064201..bae0ae08d961 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -1,10 +1,8 @@ +import { theme } from "constants/DefaultTheme"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import React, { useMemo } from "react"; import styled from "styled-components"; -import { theme } from "constants/DefaultTheme"; import { useCanvasDragging } from "utils/hooks/useCanvasDragging"; -import { useEffect } from "react"; -import { getNearestParentCanvas } from "utils/generators"; const StyledSelectionCanvas = styled.div<{ paddingBottom: number }>` position: absolute; @@ -27,6 +25,7 @@ export interface SelectedArenaDimensions { } export interface CanvasDraggingArenaProps { + canExtend: boolean; noPad?: boolean; snapColumnSpace: number; snapRows: number; @@ -35,6 +34,7 @@ export interface CanvasDraggingArenaProps { } export function CanvasDraggingArena({ + canExtend, noPad, snapColumnSpace, snapRows, @@ -48,6 +48,7 @@ export function CanvasDraggingArena({ const canvasRef = React.useRef(null); const canvasDrawRef = React.useRef(null); const { showCanvas } = useCanvasDragging(canvasRef, canvasDrawRef, { + canExtend, noPad, snapColumnSpace, snapRows, @@ -57,14 +58,11 @@ export function CanvasDraggingArena({ return showCanvas ? ( <> - + diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 6adf95b6e128..00eec9effd28 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -14,7 +14,6 @@ import { getCurrentPageId, } from "selectors/editorSelectors"; import styled from "styled-components"; -import { getNearestParentCanvas } from "utils/generators"; const StyledSelectionCanvas = styled.canvas` position: absolute; @@ -55,9 +54,6 @@ export function CanvasSelectionArena({ const mainContainer = useSelector((state: AppState) => getWidget(state, widgetId), ); - const scrollParent: Element | null = getNearestParentCanvas( - canvasRef.current, - ); const currentPageId = useSelector(getCurrentPageId); const appLayout = useSelector(getCurrentApplicationLayout); const throttledWidgetSelection = useCallback( @@ -226,9 +222,6 @@ export function CanvasSelectionArena({ } }; const onMouseMove = (e: any) => { - const { height = 0, width = 0 } = - scrollParent?.getBoundingClientRect() || {}; - if (isDragging && canvasRef.current) { selectionRectangle.width = e.offsetX - canvasRef.current.offsetLeft - selectionRectangle.left; @@ -265,7 +258,6 @@ export function CanvasSelectionArena({ snapRows, // mainContainer.minHeight, ]); - const { height = 0, width = 0 } = scrollParent?.getBoundingClientRect() || {}; return appMode === APP_MODE.EDIT && !isDragging ? ( <> diff --git a/app/client/src/utils/generators.tsx b/app/client/src/utils/generators.tsx index bfca8af51e07..ea18eede87f1 100644 --- a/app/client/src/utils/generators.tsx +++ b/app/client/src/utils/generators.tsx @@ -1,3 +1,4 @@ +import { WidgetType } from "constants/WidgetConstants"; import generate from "nanoid/generate"; const ALPHANUMERIC = "1234567890abcdefghijklmnopqrstuvwxyz"; @@ -26,6 +27,12 @@ export const getNearestParentCanvas = (el: Element | null) => { return null; }; +export const getNearestWidget = (el: Element | null, type: WidgetType) => { + const canvasQuerySelector = `div[type="${type}"]`; + if (el) return el.closest(canvasQuerySelector); + return null; +}; + export default { generateReactKey, generateClassName, diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index a3d5f48d84b1..6d778d1860c7 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -2,12 +2,10 @@ import { getSnappedXY } from "components/editorComponents/Dropzone"; import { CONTAINER_GRID_PADDING, GridDefaults, - MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { debounce, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena"; import { useEffect } from "react"; -import { height } from "styled-system"; import { getNearestParentCanvas } from "utils/generators"; import { noCollision } from "utils/WidgetPropsUtils"; import { useWidgetDragResize } from "./dragResizeHooks"; @@ -21,6 +19,7 @@ export const useCanvasDragging = ( canvasRef: React.RefObject, canvasDrawRef: React.RefObject, { + canExtend, noPad, snapColumnSpace, snapRows, @@ -43,6 +42,7 @@ export const useCanvasDragging = ( rowRef, updateRows, } = useBlocksToBeDraggedOnCanvas({ + canExtend, noPad, snapColumnSpace, snapRows, @@ -54,22 +54,23 @@ export const useCanvasDragging = ( setDraggingNewWidget, setDraggingState, } = useWidgetDragResize(); + const updateCanvasPosition = () => { - const scrollParent: Element | null = getNearestParentCanvas( + const parentCanvas: Element | null = getNearestParentCanvas( canvasRef.current, ); - if (scrollParent && canvasDrawRef.current) { - const { devicePixelRatio: scale = 1 } = window; - const { height, width } = scrollParent.getBoundingClientRect(); + if (parentCanvas && canvasDrawRef.current && canvasRef.current) { + const { height } = parentCanvas.getBoundingClientRect(); + const { width } = canvasRef.current.getBoundingClientRect(); canvasDrawRef.current.style.width = width + "px"; + canvasDrawRef.current.style.position = canExtend ? "absolute" : "sticky"; + canvasDrawRef.current.style.left = "0px"; canvasDrawRef.current.style.height = height + "px"; - canvasDrawRef.current.width = width * scale; - canvasDrawRef.current.height = height * scale; - canvasDrawRef.current.style.top = scrollParent.scrollTop + "px"; - const canvasCtx: any = canvasDrawRef.current.getContext("2d"); - canvasCtx.scale(scale, scale); + canvasDrawRef.current.style.top = + (canExtend ? parentCanvas.scrollTop : 0) + "px"; } }; + const canScroll = useCanvasDragToScroll( canvasRef, isCurrentDraggedCanvas, @@ -90,7 +91,6 @@ export const useCanvasDragging = ( // const scale = 1; let canvasIsDragging = false; let isUpdatingRows = false; - let animationFrameId: number; let currentRectanglesToDraw: WidgetDraggingBlock[] = []; const scrollObj: any = {}; @@ -203,10 +203,6 @@ export const useCanvasDragging = ( }; }); canvasCtx.save(); - // canvasRef.current.style.height = - // (rowRef.current * snapRowSpace + - // (widgetId === MAIN_CONTAINER_WIDGET_ID ? 200 : 0)) * - // scale; canvasCtx.scale(scale, scale); canvasCtx.clearRect( 0, @@ -258,8 +254,6 @@ export const useCanvasDragging = ( canScroll.current = false; debouncedFn.cancel(); canScroll.current = true; - - // debouncedFn(); }, 50, { @@ -272,6 +266,7 @@ export const useCanvasDragging = ( if ( canvasRef.current && isCurrentDraggedCanvas && + canvasIsDragging && canvasDrawRef.current ) { const canvasCtx: any = canvasDrawRef.current.getContext("2d"); @@ -289,25 +284,20 @@ export const useCanvasDragging = ( }); } canvasCtx.restore(); - animationFrameId = window.requestAnimationFrame(renderBlocks); } }; const drawBlockOnCanvas = (blockDimensions: WidgetDraggingBlock) => { - if (canvasDrawRef.current && canvasRef.current && scrollParent) { + if ( + canvasDrawRef.current && + canvasRef.current && + scrollParent && + isCurrentDraggedCanvas && + canvasIsDragging + ) { const canvasCtx: any = canvasDrawRef.current.getContext("2d"); - const { top } = canvasRef.current.getBoundingClientRect(); - const { - height: parentHeight, - top: parentTop, - } = scrollParent.getBoundingClientRect(); - const offSetCozOfPadding = - parentHeight + parentTop + scrollParent.scrollTop > - scrollParent.scrollHeight - ? scrollParent.scrollHeight - - (parentHeight + parentTop + scrollParent.scrollTop) - : 0; - const topOffset = scrollParent.scrollTop; + const topOffset = canExtend ? scrollParent.scrollTop : 0; + const misplacedCozOfScroll = topOffset % snapRowSpace; const snappedXY = getSnappedXY( snapColumnSpace, snapRowSpace, @@ -340,7 +330,10 @@ export const useCanvasDragging = ( canvasCtx.strokeStyle = "rgb(104, 113, 239)"; canvasCtx.strokeRect( snappedXY.X + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), - snappedXY.Y + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), + snappedXY.Y - + misplacedCozOfScroll + + strokeWidth + + (noPad ? 0 : CONTAINER_GRID_PADDING), blockDimensions.width - strokeWidth, blockDimensions.height - strokeWidth, ); @@ -397,8 +390,12 @@ export const useCanvasDragging = ( window.addEventListener("mouseup", onMouseUp, false); }; const startDragging = () => { - if (canvasRef.current && canvasDrawRef.current) { + if (canvasRef.current && canvasDrawRef.current && scrollParent) { + const { height } = scrollParent.getBoundingClientRect(); + const { width } = canvasRef.current.getBoundingClientRect(); const canvasCtx: any = canvasDrawRef.current.getContext("2d"); + canvasDrawRef.current.width = width * scale; + canvasDrawRef.current.height = height * scale; canvasCtx.scale(scale, scale); updateCanvasPosition(); initializeListeners(); @@ -415,7 +412,6 @@ export const useCanvasDragging = ( startDragging(); return () => { - window.cancelAnimationFrame(animationFrameId); canvasRef.current?.removeEventListener("mousemove", onMouseMove); canvasRef.current?.removeEventListener("mouseup", onMouseUp); scrollParent?.removeEventListener("scroll", onScroll); diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index 0c7393fc91c3..960118f98b2f 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -131,15 +131,16 @@ class ContainerWidget extends BaseWidget< <> - {/* */} + /> )} Date: Mon, 19 Jul 2021 10:13:46 +0530 Subject: [PATCH 34/68] dip --- .../editorComponents/DropTargetComponent.tsx | 5 ++++- .../src/pages/Editor/WidgetsMultiSelectBox.tsx | 8 ++++---- .../src/utils/hooks/useCanvasDragToScroll.ts | 3 ++- app/client/src/utils/hooks/useCanvasDragging.ts | 14 ++++++++------ app/client/src/widgets/ContainerWidget.tsx | 1 + 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index b272c3605950..05881e86b7c2 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -99,6 +99,9 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); rowRef.current = snapRows; updateHeight(); + if (canDropTargetExtend) { + updateCanvasSnapRows(props.widgetId, snapRows); + } }, [props.bottomRow, props.canExtend]); const persistDropTargetRows = (widgetId: string, widgetBottomRow: number) => { @@ -130,7 +133,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { dropTargetRef.current.style.height = height; } }), - [], + [canDropTargetExtend], ); /* Update the rows of the main container based on the current widget's (dragging/resizing) bottom row */ diff --git a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx index 5b5d53255fc4..33b5b4aa98b4 100644 --- a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx +++ b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx @@ -158,6 +158,8 @@ interface OffsetBox { function WidgetsMultiSelectBox(props: { widgetId: string; widgetType: string; + snapColumnSpace: number; + snapRowSpace: number; }): any { const dispatch = useDispatch(); const canvasWidgets = useSelector(getCanvasWidgets); @@ -201,12 +203,10 @@ function WidgetsMultiSelectBox(props: { e.stopPropagation(); if (draggableRef.current) { const bounds = draggableRef.current.getBoundingClientRect(); - const parentRowSpace = get(selectedWidgets, "0.parentRowSpace"); - const parentColumnSpace = get(selectedWidgets, "0.parentColumnSpace"); const parentId = get(selectedWidgets, "0.parentId"); const startPoints = { - top: (e.clientY - bounds.top) / parentRowSpace, - left: (e.clientX - bounds.left) / parentColumnSpace, + top: (e.clientY - bounds.top) / props.snapRowSpace, + left: (e.clientX - bounds.left) / props.snapColumnSpace, }; const top = minBy(selectedWidgets, (rect) => rect.topRow)?.topRow; const left = minBy(selectedWidgets, (rect) => rect.leftColumn) diff --git a/app/client/src/utils/hooks/useCanvasDragToScroll.ts b/app/client/src/utils/hooks/useCanvasDragToScroll.ts index ddd046726476..e600250fa8e9 100644 --- a/app/client/src/utils/hooks/useCanvasDragToScroll.ts +++ b/app/client/src/utils/hooks/useCanvasDragToScroll.ts @@ -8,6 +8,7 @@ export const useCanvasDragToScroll = ( isCurrentDraggedCanvas: boolean, isDragging: boolean, snapRows: number, + canExtend: boolean, ) => { const canScroll = useRef(true); useEffect(() => { @@ -92,6 +93,6 @@ export const useCanvasDragToScroll = ( canvasRef.current?.removeEventListener("mousemove", checkIfNeedsScroll); }; } - }, [isCurrentDraggedCanvas, snapRows]); + }, [isCurrentDraggedCanvas, snapRows, canExtend]); return canScroll; }; diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index 6d778d1860c7..a5d119480674 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -27,6 +27,8 @@ export const useCanvasDragging = ( widgetId, }: CanvasDraggingArenaProps, ) => { + const { devicePixelRatio: scale = 1 } = window; + const { blocksToDraw, defaultHandlePositions, @@ -55,7 +57,7 @@ export const useCanvasDragging = ( setDraggingState, } = useWidgetDragResize(); - const updateCanvasPosition = () => { + const updateCanvasStyles = () => { const parentCanvas: Element | null = getNearestParentCanvas( canvasRef.current, ); @@ -76,6 +78,7 @@ export const useCanvasDragging = ( isCurrentDraggedCanvas, isDragging, snapRows, + canExtend, ); useEffect(() => { if ( @@ -87,8 +90,6 @@ export const useCanvasDragging = ( const scrollParent: Element | null = getNearestParentCanvas( canvasRef.current, ); - const { devicePixelRatio: scale = 1 } = window; - // const scale = 1; let canvasIsDragging = false; let isUpdatingRows = false; let currentRectanglesToDraw: WidgetDraggingBlock[] = []; @@ -368,7 +369,7 @@ export const useCanvasDragging = ( const initializeListeners = () => { canvasRef.current?.addEventListener("mousemove", onMouseMove, false); canvasRef.current?.addEventListener("mouseup", onMouseUp, false); - scrollParent?.addEventListener("scroll", updateCanvasPosition, false); + scrollParent?.addEventListener("scroll", updateCanvasStyles, false); scrollParent?.addEventListener("scroll", onScroll, false); canvasRef.current?.addEventListener( @@ -397,7 +398,7 @@ export const useCanvasDragging = ( canvasDrawRef.current.width = width * scale; canvasDrawRef.current.height = height * scale; canvasCtx.scale(scale, scale); - updateCanvasPosition(); + updateCanvasStyles(); initializeListeners(); if (canvasIsDragging) { blocksToDraw.forEach((each) => { @@ -414,6 +415,7 @@ export const useCanvasDragging = ( return () => { canvasRef.current?.removeEventListener("mousemove", onMouseMove); canvasRef.current?.removeEventListener("mouseup", onMouseUp); + scrollParent?.removeEventListener("scroll", updateCanvasStyles); scrollParent?.removeEventListener("scroll", onScroll); canvasRef.current?.removeEventListener( "mouseover", @@ -431,7 +433,7 @@ export const useCanvasDragging = ( resetCanvasState(); } } - }, [isDragging, isResizing, blocksToDraw, snapRows]); + }, [isDragging, isResizing, blocksToDraw, snapRows, canExtend]); return { showCanvas: isDragging && !isResizing, }; diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index 960118f98b2f..961201d340cb 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -144,6 +144,7 @@ class ContainerWidget extends BaseWidget< )} From 2f9a130cec788aad9d94755fe7d170e94ad84b37 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 19 Jul 2021 11:38:58 +0530 Subject: [PATCH 35/68] dip --- .../src/pages/common/CanvasSelectionArena.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 00eec9effd28..b8e85534d5ad 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -83,6 +83,9 @@ export function CanvasSelectionArena({ ); useEffect(() => { if (appMode === APP_MODE.EDIT && !isDragging && canvasRef.current) { + // ToDo: Needs a repositioning canvas window to limit the highest number of pixels rendered for an application of any height. + // as of today (Pixels rendered by canvas) ∝ (Application height) so as height increases will run into to dead renders. + // https://on690.codesandbox.io/ to check the number of pixels limit supported for a canvas // const { devicePixelRatio: scale = 1 } = window; const scale = 1; @@ -260,14 +263,11 @@ export function CanvasSelectionArena({ ]); return appMode === APP_MODE.EDIT && !isDragging ? ( - <> - - {/* */} - + ) : null; } CanvasSelectionArena.displayName = "CanvasSelectionArena"; From e72cb89977b4b7c6c342f3af2b52a83905f24c24 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 19 Jul 2021 17:06:57 +0530 Subject: [PATCH 36/68] middle ware for dropping multiple widgets. --- app/client/src/actions/pageActions.tsx | 14 --- .../src/constants/ReduxActionConstants.tsx | 2 +- .../reducers/uiReducers/dragResizeReducer.ts | 2 +- app/client/src/sagas/DraggingCanvasSagas.ts | 118 ++++++++++++++++++ app/client/src/sagas/WidgetOperationSagas.tsx | 71 +---------- app/client/src/sagas/index.tsx | 4 +- app/client/src/sagas/selectors.tsx | 4 + .../hooks/useBlocksToBeDraggedOnCanvas.ts | 65 +++++++--- .../src/utils/hooks/useCanvasDragging.ts | 12 ++ 9 files changed, 190 insertions(+), 102 deletions(-) create mode 100644 app/client/src/sagas/DraggingCanvasSagas.ts diff --git a/app/client/src/actions/pageActions.tsx b/app/client/src/actions/pageActions.tsx index 177661dd016e..8c92b5b2630a 100644 --- a/app/client/src/actions/pageActions.tsx +++ b/app/client/src/actions/pageActions.tsx @@ -192,19 +192,6 @@ export type WidgetAddChild = { props?: Record; }; -export type WidgetMove = { - widgetId: string; - leftColumn: number; - topRow: number; - parentId: string; - /* - If newParentId is different from what we have in redux store, - then we have to delete this, - as it has been dropped in another container somewhere. - */ - newParentId: string; -}; - export type WidgetRemoveChild = { widgetId: string; childWidgetId: string; @@ -259,7 +246,6 @@ export const updateWidget = ( payload: any, ): ReduxAction< | WidgetAddChild - | WidgetMove | WidgetResize | WidgetDelete | WidgetAddChildren diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index 526ee198d014..1a041950dd1f 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -151,7 +151,7 @@ export const ReduxActionTypes: { [key: string]: string } = { WIDGET_ADD_CHILD: "WIDGET_ADD_CHILD", WIDGET_CHILD_ADDED: "WIDGET_CHILD_ADDED", WIDGET_REMOVE_CHILD: "WIDGET_REMOVE_CHILD", - WIDGET_MOVE: "WIDGET_MOVE", + WIDGETS_MOVE: "WIDGETS_MOVE", WIDGET_RESIZE: "WIDGET_RESIZE", WIDGET_DELETE: "WIDGET_DELETE", WIDGET_BULK_DELETE: "WIDGET_BULK_DELETE", diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index cc1e24e755a1..0ebe32b93afa 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -142,7 +142,7 @@ type DraggingGroupCenter = { top?: number; left?: number; }; -type DragDetails = { +export type DragDetails = { dragGroupActualParent?: string; draggingGroupCenter?: DraggingGroupCenter; newWidget?: any; diff --git a/app/client/src/sagas/DraggingCanvasSagas.ts b/app/client/src/sagas/DraggingCanvasSagas.ts new file mode 100644 index 000000000000..12d64aca5fee --- /dev/null +++ b/app/client/src/sagas/DraggingCanvasSagas.ts @@ -0,0 +1,118 @@ +import { Toaster } from "components/ads/Toast"; +import { + ReduxAction, + ReduxActionErrorTypes, + ReduxActionTypes, +} from "constants/ReduxActionConstants"; +import { + CanvasWidgetsReduxState, + FlattenedWidgetProps, +} from "reducers/entityReducers/canvasWidgetsReducer"; +import { all, put, select, takeLatest } from "redux-saga/effects"; +import { WidgetDraggingUpdateParams } from "utils/hooks/useBlocksToBeDraggedOnCanvas"; +import { updateWidgetPosition } from "utils/WidgetPropsUtils"; +import { getWidgets } from "./selectors"; +import log from "loglevel"; +import { cloneDeep } from "lodash"; +import { updateAndSaveLayout } from "actions/pageActions"; + +export type WidgetMoveParams = { + widgetId: string; + leftColumn: number; + topRow: number; + parentId: string; + /* + If newParentId is different from what we have in redux store, + then we have to delete this, + as it has been dropped in another container somewhere. + */ + newParentId: string; + allWidgets: CanvasWidgetsReduxState; +}; + +function* moveWidgetsSaga( + actionPayload: ReduxAction<{ + draggedBlocksToUpdate: WidgetDraggingUpdateParams[]; + }>, +) { + const start = performance.now(); + + const { draggedBlocksToUpdate } = actionPayload.payload; + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const widgets = cloneDeep(allWidgets); + try { + const updatedWidgets = draggedBlocksToUpdate.reduce((widgetsObj, each) => { + return moveWidget({ + ...each.updateWidgetParams.payload, + widgetId: each.widgetId, + allWidgets: widgetsObj, + }); + }, widgets); + yield put(updateAndSaveLayout(updatedWidgets)); + log.debug("move computations took", performance.now() - start, "ms"); + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: ReduxActionTypes.WIDGETS_MOVE, + error, + }, + }); + } +} + +function moveWidget(widgetMoveParams: WidgetMoveParams) { + Toaster.clear(); + const { + allWidgets, + leftColumn, + newParentId, + parentId, + topRow, + widgetId, + } = widgetMoveParams; + const stateWidget: FlattenedWidgetProps = allWidgets[widgetId]; + let widget = Object.assign({}, stateWidget); + // Get all widgets from DSL/Redux Store + const stateWidgets: CanvasWidgetsReduxState = cloneDeep(allWidgets); + const widgets = Object.assign({}, stateWidgets); + // Get parent from DSL/Redux Store + const stateParent: FlattenedWidgetProps = allWidgets[parentId]; + const parent = { + ...stateParent, + children: [...(stateParent.children || [])], + }; + // Update position of widget + const updatedPosition = updateWidgetPosition(widget, leftColumn, topRow); + widget = { ...widget, ...updatedPosition }; + + // Replace widget with update widget props + widgets[widgetId] = widget; + // If the parent has changed i.e parentWidgetId is not parent.widgetId + if (parent.widgetId !== newParentId && widgetId !== newParentId) { + // Remove from the previous parent + + if (parent.children && Array.isArray(parent.children)) { + const indexOfChild = parent.children.indexOf(widgetId); + if (indexOfChild > -1) delete parent.children[indexOfChild]; + parent.children = parent.children.filter(Boolean); + } + + // Add to new parent + + widgets[parent.widgetId] = parent; + const newParent = { + ...widgets[newParentId], + children: widgets[newParentId].children + ? [...(widgets[newParentId].children || []), widgetId] + : [widgetId], + }; + widgets[widgetId].parentId = newParentId; + widgets[newParentId] = newParent; + } + return widgets; +} + +export default function* draggingCanvasSagas() { + yield all([takeLatest(ReduxActionTypes.WIDGETS_MOVE, moveWidgetsSaga)]); +} diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 6ab1642e576c..cfd9cb76ec06 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -9,7 +9,6 @@ import { WidgetAddChild, WidgetAddChildren, WidgetDelete, - WidgetMove, WidgetResize, } from "actions/pageActions"; import { @@ -22,10 +21,7 @@ import { getWidget, getWidgets, } from "./selectors"; -import { - generateWidgetProps, - updateWidgetPosition, -} from "utils/WidgetPropsUtils"; +import { generateWidgetProps } from "utils/WidgetPropsUtils"; import { all, call, @@ -858,70 +854,6 @@ export function* undoDeleteSaga(action: ReduxAction<{ widgetId: string }>) { } } -export function* moveSaga(moveAction: ReduxAction) { - try { - Toaster.clear(); - const start = performance.now(); - const { - leftColumn, - newParentId, - parentId, - topRow, - widgetId, - } = moveAction.payload; - const stateWidget: FlattenedWidgetProps = yield select(getWidget, widgetId); - let widget = Object.assign({}, stateWidget); - // Get all widgets from DSL/Redux Store - const stateWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const widgets = Object.assign({}, stateWidgets); - // Get parent from DSL/Redux Store - const stateParent: FlattenedWidgetProps = yield select(getWidget, parentId); - const parent = { - ...stateParent, - children: [...(stateParent.children || [])], - }; - // Update position of widget - const updatedPosition = updateWidgetPosition(widget, leftColumn, topRow); - widget = { ...widget, ...updatedPosition }; - - // Replace widget with update widget props - widgets[widgetId] = widget; - // If the parent has changed i.e parentWidgetId is not parent.widgetId - if (parent.widgetId !== newParentId && widgetId !== newParentId) { - // Remove from the previous parent - - if (parent.children && Array.isArray(parent.children)) { - const indexOfChild = parent.children.indexOf(widgetId); - if (indexOfChild > -1) delete parent.children[indexOfChild]; - parent.children = parent.children.filter(Boolean); - } - - // Add to new parent - - widgets[parent.widgetId] = parent; - const newParent = { - ...widgets[newParentId], - children: widgets[newParentId].children - ? [...(widgets[newParentId].children || []), widgetId] - : [widgetId], - }; - widgets[widgetId].parentId = newParentId; - widgets[newParentId] = newParent; - } - log.debug("move computations took", performance.now() - start, "ms"); - - yield put(updateAndSaveLayout(widgets)); - } catch (error) { - yield put({ - type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, - payload: { - action: ReduxActionTypes.WIDGET_MOVE, - error, - }, - }); - } -} - export function* resizeSaga(resizeAction: ReduxAction) { try { Toaster.clear(); @@ -1908,7 +1840,6 @@ export default function* widgetOperationSagas() { ReduxActionTypes.WIDGET_BULK_DELETE, deleteAllSelectedWidgetsSaga, ), - takeLatest(ReduxActionTypes.WIDGET_MOVE, moveSaga), takeLatest(ReduxActionTypes.WIDGET_RESIZE, resizeSaga), takeEvery( ReduxActionTypes.UPDATE_WIDGET_PROPERTY_REQUEST, diff --git a/app/client/src/sagas/index.tsx b/app/client/src/sagas/index.tsx index b01b9e92c40e..5c38d44ad680 100644 --- a/app/client/src/sagas/index.tsx +++ b/app/client/src/sagas/index.tsx @@ -32,9 +32,10 @@ import debuggerSagas from "./DebuggerSagas"; import tourSagas from "./TourSagas"; import notificationsSagas from "./NotificationsSagas"; import selectionCanvasSagas from "./SelectionCanvasSagas"; +import draggingCanvasSagas from "./DraggingCanvasSagas"; + import log from "loglevel"; import * as sentry from "@sentry/react"; - const sagas = [ initSagas, pageSagas, @@ -69,6 +70,7 @@ const sagas = [ tourSagas, notificationsSagas, selectionCanvasSagas, + draggingCanvasSagas, ]; export function* rootSaga(sagasToRun = sagas) { diff --git a/app/client/src/sagas/selectors.tsx b/app/client/src/sagas/selectors.tsx index 82559b7bab7c..071283e8dfc6 100644 --- a/app/client/src/sagas/selectors.tsx +++ b/app/client/src/sagas/selectors.tsx @@ -132,6 +132,10 @@ export const getPluginIdOfPackageName = ( return undefined; }; +export const getDragDetails = (state: AppState) => { + return state.ui.widgetDragResize.dragDetails; +}; + export const getSelectedWidget = (state: AppState) => { const selectedWidgetId = state.ui.widgetDragResize.lastSelectedWidget; if (!selectedWidgetId) return; diff --git a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts index 4e789b27869b..f15e8436e8b8 100644 --- a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -9,16 +9,23 @@ import { AppState } from "reducers"; import { getSelectedWidgets } from "selectors/ui"; import { getOccupiedSpaces } from "selectors/editorSelectors"; import { OccupiedSpace } from "constants/editorConstants"; -import { getWidgets } from "sagas/selectors"; +import { getDragDetails, getWidgets } from "sagas/selectors"; import { getDropZoneOffsets, + WidgetOperationParams, widgetOperationParams, } from "utils/WidgetPropsUtils"; import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; import { XYCoord } from "react-dnd"; -import { EditorContext } from "components/editorComponents/EditorContextProvider"; import { isEmpty } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena"; +import { useDispatch } from "react-redux"; +import { ReduxActionTypes } from "constants/ReduxActionConstants"; +import { EditorContext } from "components/editorComponents/EditorContextProvider"; + +export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { + updateWidgetParams: WidgetOperationParams; +} export type WidgetDraggingBlock = { left: number; @@ -38,10 +45,9 @@ export const useBlocksToBeDraggedOnCanvas = ({ snapRowSpace, widgetId, }: CanvasDraggingArenaProps) => { + const dispatch = useDispatch(); const containerPadding = noPad ? 0 : CONTAINER_GRID_PADDING; - const dragDetails = useSelector( - (state: AppState) => state.ui.widgetDragResize.dragDetails, - ); + const dragDetails = useSelector(getDragDetails); const defaultHandlePositions = { top: 20, left: 20, @@ -62,6 +68,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ const isDragging = useSelector( (state: AppState) => state.ui.widgetDragResize.isDragging, ); + const { updateWidget } = useContext(EditorContext); const allWidgets = useSelector(getWidgets); const getDragCenterSpace = () => { @@ -121,19 +128,18 @@ export const useBlocksToBeDraggedOnCanvas = ({ const { persistDropTargetRows, updateDropTargetRows } = useContext( DropTargetContext, ); - const { updateWidget } = useContext(EditorContext); const onDrop = (drawingBlocks: WidgetDraggingBlock[]) => { const cannotDrop = drawingBlocks.some((each) => { return !each.isNotColliding; }); if (!cannotDrop) { - drawingBlocks + const draggedBlocksToUpdate = drawingBlocks .sort( (each1, each2) => each1.top + each1.height - (each2.top + each2.height), ) - .forEach((each) => { + .map((each) => { const widget = newWidget ? newWidget : allWidgets[each.widgetId]; const updateWidgetParams = widgetOperationParams( widget, @@ -151,16 +157,45 @@ export const useBlocksToBeDraggedOnCanvas = ({ persistDropTargetRows && persistDropTargetRows(widget.widgetId, widgetBottomRow); - /* Finally update the widget */ - updateWidget && - updateWidget( - updateWidgetParams.operation, - updateWidgetParams.widgetId, - updateWidgetParams.payload, - ); + return { + ...each, + updateWidgetParams, + }; }); + dispatchDrop(draggedBlocksToUpdate); } }; + + const dispatchDrop = ( + draggedBlocksToUpdate: WidgetDraggingUpdateParams[], + ) => { + if (isNewWidget) { + addNewWidget(draggedBlocksToUpdate[0]); + } else { + bulkMoveWidgets(draggedBlocksToUpdate); + } + }; + + const bulkMoveWidgets = ( + draggedBlocksToUpdate: WidgetDraggingUpdateParams[], + ) => { + dispatch({ + type: ReduxActionTypes.WIDGETS_MOVE, + payload: { + draggedBlocksToUpdate, + }, + }); + }; + + const addNewWidget = (newWidget: WidgetDraggingUpdateParams) => { + const { updateWidgetParams } = newWidget; + updateWidget && + updateWidget( + updateWidgetParams.operation, + updateWidgetParams.widgetId, + updateWidgetParams.payload, + ); + }; const updateRows = (drawingBlocks: WidgetDraggingBlock[], rows: number) => { if (drawingBlocks.length) { const sortedByTopBlocks = drawingBlocks.sort( diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index a5d119480674..ed086f025bc8 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -201,6 +201,18 @@ export const useCanvasDragging = ( ...each, left: each.left + delta.left, top: each.top + delta.top, + isNotColliding: noCollision( + { x: each.left + delta.left, y: each.top + delta.top }, + snapColumnSpace, + snapRowSpace, + { x: 0, y: 0 }, + each.columnWidth, + each.rowHeight, + each.widgetId, + occSpaces, + rowRef.current, + GridDefaults.DEFAULT_GRID_COLUMNS, + ), }; }); canvasCtx.save(); From c5e9d1bcc4cd05d73db7599a95779f01055d11c2 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 19 Jul 2021 17:37:22 +0530 Subject: [PATCH 37/68] adding scroll to drag for canvas selection. --- .../editorComponents/DropTargetComponent.tsx | 2 +- .../src/pages/common/CanvasSelectionArena.tsx | 50 +++++++++++++++++++ .../uiReducers/canvasSelectionReducer.ts | 9 +++- .../src/utils/hooks/useCanvasDragToScroll.ts | 4 +- app/client/src/widgets/ContainerWidget.tsx | 1 + 5 files changed, 61 insertions(+), 5 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 05881e86b7c2..b6db15264790 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -196,7 +196,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { {!(childWidgets && childWidgets.length) && !isDragging && !props.parentId && } - {(isDragging || isResizing) && draggedOn === props.widgetId && ( + {((isDragging && draggedOn === props.widgetId) || isResizing) && ( { + return state.ui.canvasSelection.isDraggingForSelection; + }); + const isCurrentWidgetDrawing = useSelector((state: AppState) => { + return state.ui.canvasSelection.widgetId === widgetId; + }); + useCanvasDragToScroll( + canvasRef, + isCurrentWidgetDrawing, + isDraggingForSelection, + snapRows, + canExtend, + ); useEffect(() => { if (appMode === APP_MODE.EDIT && !isDragging && canvasRef.current) { // ToDo: Needs a repositioning canvas window to limit the highest number of pixels rendered for an application of any height. @@ -88,6 +105,10 @@ export function CanvasSelectionArena({ // https://on690.codesandbox.io/ to check the number of pixels limit supported for a canvas // const { devicePixelRatio: scale = 1 } = window; const scale = 1; + const scrollParent: Element | null = getNearestParentCanvas( + canvasRef.current, + ); + const scrollObj: any = {}; let canvasCtx: any = canvasRef.current.getContext("2d"); const initRectangle = (): SelectedArenaDimensions => ({ @@ -116,6 +137,7 @@ export function CanvasSelectionArena({ canvasRef.current.addEventListener("mousemove", onMouseMove, false); canvasRef.current.addEventListener("mouseleave", onMouseLeave, false); canvasRef.current.addEventListener("mouseenter", onMouseEnter, false); + scrollParent?.addEventListener("scroll", onScroll, false); } }; @@ -239,6 +261,34 @@ export function CanvasSelectionArena({ const selectionDimensions = getSelectionDimensions(); drawRectangle(selectionDimensions); selectWidgetsInit(selectionDimensions, isMultiSelect); + scrollObj.lastMouseMoveEvent = e; + scrollObj.lastScrollTop = scrollParent?.scrollTop; + scrollObj.lastScrollHeight = scrollParent?.scrollHeight; + } + }; + const onScroll = () => { + const { + lastMouseMoveEvent, + lastScrollHeight, + lastScrollTop, + } = scrollObj; + if ( + lastMouseMoveEvent && + Number.isInteger(lastScrollHeight) && + Number.isInteger(lastScrollTop) && + scrollParent + ) { + const delta = + scrollParent?.scrollHeight + + scrollParent?.scrollTop - + (lastScrollHeight + lastScrollTop); + if (delta) { + console.count("onScroll"); + } + onMouseMove({ + offsetX: lastMouseMoveEvent.offsetX, + offsetY: lastMouseMoveEvent.offsetY + delta, + }); } }; if (appMode === APP_MODE.EDIT) { diff --git a/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts b/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts index 8d1d3ebde772..ff1f6cd652af 100644 --- a/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts +++ b/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts @@ -1,13 +1,17 @@ import { createImmerReducer } from "utils/AppsmithUtils"; -import { ReduxActionTypes } from "constants/ReduxActionConstants"; +import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; const initialState: CanvasSelectionState = { isDraggingForSelection: false, }; export const canvasSelectionReducer = createImmerReducer(initialState, { - [ReduxActionTypes.START_CANVAS_SELECTION]: (state: CanvasSelectionState) => { + [ReduxActionTypes.START_CANVAS_SELECTION]: ( + state: CanvasSelectionState, + action: ReduxAction<{ widgetId?: string }>, + ) => { state.isDraggingForSelection = true; + state.widgetId = action.payload.widgetId; }, [ReduxActionTypes.STOP_CANVAS_SELECTION]: (state: CanvasSelectionState) => { state.isDraggingForSelection = false; @@ -16,6 +20,7 @@ export const canvasSelectionReducer = createImmerReducer(initialState, { export type CanvasSelectionState = { isDraggingForSelection: boolean; + widgetId?: string; }; export default canvasSelectionReducer; diff --git a/app/client/src/utils/hooks/useCanvasDragToScroll.ts b/app/client/src/utils/hooks/useCanvasDragToScroll.ts index e600250fa8e9..e836dc79bf26 100644 --- a/app/client/src/utils/hooks/useCanvasDragToScroll.ts +++ b/app/client/src/utils/hooks/useCanvasDragToScroll.ts @@ -4,7 +4,7 @@ import { getNearestParentCanvas } from "utils/generators"; import { getScrollByPixels } from "utils/helpers"; export const useCanvasDragToScroll = ( - canvasRef: RefObject, + canvasRef: RefObject, isCurrentDraggedCanvas: boolean, isDragging: boolean, snapRows: number, @@ -93,6 +93,6 @@ export const useCanvasDragToScroll = ( canvasRef.current?.removeEventListener("mousemove", checkIfNeedsScroll); }; } - }, [isCurrentDraggedCanvas, snapRows, canExtend]); + }, [isCurrentDraggedCanvas, isDragging, snapRows, canExtend]); return canScroll; }; diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index 961201d340cb..3412be55f7cf 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -138,6 +138,7 @@ class ContainerWidget extends BaseWidget< /> From 8e32f286a695b6d4db15d44123e7cc352bad5bfc Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 19 Jul 2021 18:15:22 +0530 Subject: [PATCH 38/68] fixing drag disabled and modal widget(detach from layout) drops --- .../src/pages/common/CanvasDraggingArena.tsx | 4 ++ .../src/pages/common/CanvasSelectionArena.tsx | 9 +-- app/client/src/utils/WidgetPropsUtils.tsx | 4 ++ .../hooks/useBlocksToBeDraggedOnCanvas.ts | 2 + .../src/utils/hooks/useCanvasDragging.ts | 55 +++++++++++-------- app/client/src/widgets/ContainerWidget.tsx | 1 + 6 files changed, 43 insertions(+), 32 deletions(-) diff --git a/app/client/src/pages/common/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasDraggingArena.tsx index bae0ae08d961..2c0b680b5798 100644 --- a/app/client/src/pages/common/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasDraggingArena.tsx @@ -26,6 +26,8 @@ export interface SelectedArenaDimensions { export interface CanvasDraggingArenaProps { canExtend: boolean; + detachFromLayout?: boolean; + dropDisabled?: boolean; noPad?: boolean; snapColumnSpace: number; snapRows: number; @@ -35,6 +37,7 @@ export interface CanvasDraggingArenaProps { export function CanvasDraggingArena({ canExtend, + dropDisabled = false, noPad, snapColumnSpace, snapRows, @@ -49,6 +52,7 @@ export function CanvasDraggingArena({ const canvasDrawRef = React.useRef(null); const { showCanvas } = useCanvasDragging(canvasRef, canvasDrawRef, { canExtend, + dropDisabled, noPad, snapColumnSpace, snapRows, diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 5fb152d3a0e9..9b4c991f299b 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -303,14 +303,7 @@ export function CanvasSelectionArena({ canvasRef.current?.removeEventListener("click", onClick); }; } - }, [ - appLayout, - currentPageId, - mainContainer, - isDragging, - snapRows, - // mainContainer.minHeight, - ]); + }, [appLayout, currentPageId, mainContainer, isDragging, snapRows]); return appMode === APP_MODE.EDIT && !isDragging ? ( { + if (detachFromLayout) { + return true; + } if (clientOffset && dropTargetOffset) { const [left, top] = getDropZoneOffsets( colWidth, diff --git a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts index f15e8436e8b8..7dabab6b2df0 100644 --- a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -36,6 +36,7 @@ export type WidgetDraggingBlock = { rowHeight: number; widgetId: string; isNotColliding: boolean; + detachFromLayout?: boolean; }; export const useBlocksToBeDraggedOnCanvas = ({ @@ -101,6 +102,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ columnWidth: newWidget.columns, rowHeight: newWidget.rows, widgetId: newWidget.widgetId, + detachFromLayout: newWidget.detachFromLayout, isNotColliding: true, }, ]; diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index ed086f025bc8..a0f2975469dd 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -20,6 +20,7 @@ export const useCanvasDragging = ( canvasDrawRef: React.RefObject, { canExtend, + dropDisabled, noPad, snapColumnSpace, snapRows, @@ -164,18 +165,21 @@ export const useCanvasDragging = ( rowRef.current = newRows ? newRows : rowRef.current; currentRectanglesToDraw = drawingBlocks.map((each) => ({ ...each, - isNotColliding: noCollision( - { x: each.left, y: each.top }, - snapColumnSpace, - snapRowSpace, - { x: 0, y: 0 }, - each.columnWidth, - each.rowHeight, - each.widgetId, - occSpaces, - rowRef.current, - GridDefaults.DEFAULT_GRID_COLUMNS, - ), + isNotColliding: + !dropDisabled && + noCollision( + { x: each.left, y: each.top }, + snapColumnSpace, + snapRowSpace, + { x: 0, y: 0 }, + each.columnWidth, + each.rowHeight, + each.widgetId, + occSpaces, + rowRef.current, + GridDefaults.DEFAULT_GRID_COLUMNS, + each.detachFromLayout, + ), })); if (rowDelta && canvasRef.current) { isUpdatingRows = true; @@ -201,18 +205,21 @@ export const useCanvasDragging = ( ...each, left: each.left + delta.left, top: each.top + delta.top, - isNotColliding: noCollision( - { x: each.left + delta.left, y: each.top + delta.top }, - snapColumnSpace, - snapRowSpace, - { x: 0, y: 0 }, - each.columnWidth, - each.rowHeight, - each.widgetId, - occSpaces, - rowRef.current, - GridDefaults.DEFAULT_GRID_COLUMNS, - ), + isNotColliding: + !dropDisabled && + noCollision( + { x: each.left + delta.left, y: each.top + delta.top }, + snapColumnSpace, + snapRowSpace, + { x: 0, y: 0 }, + each.columnWidth, + each.rowHeight, + each.widgetId, + occSpaces, + rowRef.current, + GridDefaults.DEFAULT_GRID_COLUMNS, + each.detachFromLayout, + ), }; }); canvasCtx.save(); diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index 3412be55f7cf..9b810232d4c6 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -132,6 +132,7 @@ class ContainerWidget extends BaseWidget< Date: Tue, 20 Jul 2021 12:36:55 +0530 Subject: [PATCH 39/68] firefox and safari fixes --- app/client/src/utils/hooks/useCanvasDragToScroll.ts | 5 ++--- app/client/src/utils/hooks/useCanvasDragging.ts | 11 ++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/client/src/utils/hooks/useCanvasDragToScroll.ts b/app/client/src/utils/hooks/useCanvasDragToScroll.ts index e836dc79bf26..e1b0e01dc73c 100644 --- a/app/client/src/utils/hooks/useCanvasDragToScroll.ts +++ b/app/client/src/utils/hooks/useCanvasDragToScroll.ts @@ -1,4 +1,3 @@ -import { debounce } from "lodash"; import { RefObject, useEffect, useRef } from "react"; import { getNearestParentCanvas } from "utils/generators"; import { getScrollByPixels } from "utils/helpers"; @@ -52,7 +51,7 @@ export const useCanvasDragToScroll = ( scrollTimeOut.push(setTimeout(scrollFn, 100 * Math.max(0.4, speed))); } }; - const checkIfNeedsScroll = debounce((e: any) => { + const checkIfNeedsScroll = (e: any) => { if (isDragging && isCurrentDraggedCanvas) { const scrollParent: Element | null = getNearestParentCanvas( canvasRef.current, @@ -82,7 +81,7 @@ export const useCanvasDragToScroll = ( } } } - }); + }; canvasRef.current?.addEventListener( "mousemove", checkIfNeedsScroll, diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index a0f2975469dd..1aed974eb59a 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -188,7 +188,10 @@ export const useCanvasDragging = ( } else if (!isUpdatingRows) { renderBlocks(); } - scrollObj.lastMouseMoveEvent = e; + scrollObj.lastMouseMoveEvent = { + offsetX: e.offsetX, + offsetY: e.offsetY, + }; scrollObj.lastScrollTop = scrollParent?.scrollTop; scrollObj.lastScrollHeight = scrollParent?.scrollHeight; } else { @@ -234,7 +237,6 @@ export const useCanvasDragging = ( renderBlocks(); canScroll.current = false; endRenderRows.cancel(); - console.count("endRenderRows"); endRenderRows(); } }); @@ -317,13 +319,12 @@ export const useCanvasDragging = ( ) { const canvasCtx: any = canvasDrawRef.current.getContext("2d"); const topOffset = canExtend ? scrollParent.scrollTop : 0; - const misplacedCozOfScroll = topOffset % snapRowSpace; const snappedXY = getSnappedXY( snapColumnSpace, snapRowSpace, { x: blockDimensions.left, - y: blockDimensions.top - topOffset, + y: blockDimensions.top, }, { x: 0, @@ -351,7 +352,7 @@ export const useCanvasDragging = ( canvasCtx.strokeRect( snappedXY.X + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), snappedXY.Y - - misplacedCozOfScroll + + topOffset + strokeWidth + (noPad ? 0 : CONTAINER_GRID_PADDING), blockDimensions.width - strokeWidth, From a3a67c7ae099bb5062f84ca97f56a5257c14bd24 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 20 Jul 2021 17:02:22 +0530 Subject: [PATCH 40/68] rebase conflicts. --- .../components/editorComponents/DraggableComponent.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 90c7fe919c91..947a6e89e7f5 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -5,7 +5,10 @@ import { WIDGET_PADDING } from "constants/WidgetConstants"; import { useDispatch, useSelector } from "react-redux"; import { AppState } from "reducers"; import { getColorWithOpacity } from "constants/DefaultTheme"; -import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; +import { + useShowTableFilterPane, + useWidgetDragResize, +} from "utils/hooks/dragResizeHooks"; import { commentModeSelector } from "selectors/commentsSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { selectWidgetInitAction } from "actions/widgetSelectionActions"; @@ -67,7 +70,7 @@ function DraggableComponent(props: DraggableComponentProps) { // Dispatch hook handy to set any `DraggableComponent` as dragging/ not dragging // The value is boolean const { setDraggingCanvas, setDraggingState } = useWidgetDragResize(); - + const showTableFilterPane = useShowTableFilterPane(); const selectedWidgets = useSelector( (state: AppState) => state.ui.widgetDragResize.selectedWidgets, ); @@ -160,6 +163,7 @@ function DraggableComponent(props: DraggableComponentProps) { top: (e.clientY - bounds.top) / props.parentRowSpace, left: (e.clientX - bounds.left) / props.parentColumnSpace, }; + showTableFilterPane(); setDraggingCanvas(props.parentId); setDraggingState( true, From 22704182af641a1fa0c53455ba9d1ec16bdf1a12 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 21 Jul 2021 15:27:14 +0530 Subject: [PATCH 41/68] fixing broken specs. --- app/client/cypress/locators/explorerlocators.json | 2 +- app/client/cypress/support/commands.js | 6 ++++-- app/client/src/pages/Editor/WidgetCard.tsx | 12 ++++++++++-- app/client/src/sagas/WidgetOperationSagas.tsx | 1 + 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/client/cypress/locators/explorerlocators.json b/app/client/cypress/locators/explorerlocators.json index 42e0c2bb9b46..5972aaaee134 100644 --- a/app/client/cypress/locators/explorerlocators.json +++ b/app/client/cypress/locators/explorerlocators.json @@ -23,7 +23,7 @@ "editEntityField": ".bp3-editable-text-input", "entity":".t--entity-name", "addWidget":".widgets .t--entity-add-btn", - "dropHere":".appsmith_widget_0", + "dropHere":"#canvas-dragging-0", "closeWidgets":".t--close-widgets-sidebar", "addDBQueryEntity": ".datasources .t--entity-add-btn", "editEntity": ".t--entity-name input" diff --git a/app/client/cypress/support/commands.js b/app/client/cypress/support/commands.js index 7da2645d5645..c6dbb9ec012a 100644 --- a/app/client/cypress/support/commands.js +++ b/app/client/cypress/support/commands.js @@ -3,6 +3,7 @@ /* eslint-disable cypress/no-assigning-return-values */ require("cypress-file-upload"); +require("@4tw/cypress-drag-drop"); const loginPage = require("../locators/LoginPage.json"); const homePage = require("../locators/HomePage.json"); @@ -2078,8 +2079,9 @@ Cypress.Commands.add("dragAndDropToCanvas", (widgetType, { x, y }) => { .trigger("mousedown", { button: 0 }, { force: true }) .trigger("mousemove", x, y, { force: true }); cy.get(explorer.dropHere) - .trigger("mousemove", x, y) - .trigger("mouseup", x, y + 20); + .trigger("mousemove", x, y, { eventConstructor: "MouseEvent" }) + .trigger("mousemove", x, y, { eventConstructor: "MouseEvent" }) + .trigger("mouseup", x, y, { eventConstructor: "MouseEvent" }); }); Cypress.Commands.add("executeDbQuery", (queryName) => { diff --git a/app/client/src/pages/Editor/WidgetCard.tsx b/app/client/src/pages/Editor/WidgetCard.tsx index be5aa1dee1e7..afb58a4593ee 100644 --- a/app/client/src/pages/Editor/WidgetCard.tsx +++ b/app/client/src/pages/Editor/WidgetCard.tsx @@ -4,7 +4,10 @@ import blankImage from "assets/images/blank.png"; import { WidgetCardProps } from "widgets/BaseWidget"; import styled from "styled-components"; import { WidgetIcons } from "icons/WidgetIcons"; -import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; +import { + useShowPropertyPane, + useWidgetDragResize, +} from "utils/hooks/dragResizeHooks"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { generateReactKey } from "utils/generators"; import { Colors } from "constants/Colors"; @@ -74,6 +77,9 @@ export const IconLabel = styled.h5` function WidgetCard(props: CardProps) { const { setDraggingNewWidget } = useWidgetDragResize(); const { deselectAll } = useWidgetSelection(); + const showPropertyPane = useShowPropertyPane(); + const { selectWidget } = useWidgetSelection(); + // Generate a new widgetId which can be used in the future for this widget. const [widgetId, setWidgetId] = useState(generateReactKey()); const [, drag, preview] = useDrag({ @@ -87,7 +93,7 @@ function WidgetCard(props: CardProps) { setDraggingNewWidget(true, { ...props.details, widgetId }); deselectAll(); }, - end: (widget, monitor) => { + end: (widget: any, monitor) => { AnalyticsUtil.logEvent("WIDGET_CARD_DROP", { widgetType: props.details.type, widgetName: props.details.widgetCardName, @@ -96,6 +102,8 @@ function WidgetCard(props: CardProps) { // We've finished dragging, generate a new widgetId to be used for next drag. setWidgetId(generateReactKey()); setDraggingNewWidget && setDraggingNewWidget(false, undefined); + selectWidget(widget.widgetId); + showPropertyPane(widget.widgetId); }, }); diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index cfd9cb76ec06..1fb483b1ee08 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -310,6 +310,7 @@ export function* addChildSaga(addChildAction: ReduxAction) { const newWidget = childWidgetPayload.widgets[childWidgetPayload.widgetId]; const updateBottomRow = + stateParent.type === WidgetTypes.CANVAS_WIDGET && newWidget.bottomRow * newWidget.parentRowSpace > stateParent.bottomRow; // Update widgets to put back in the canvasWidgetsReducer // TODO(abhinav): This won't work if dont already have an empty children: [] From 631acf34de2d04c71251faa356886ee80b7f6af8 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 22 Jul 2021 17:00:03 +0530 Subject: [PATCH 42/68] fixing specs and adding jest tests. --- app/client/cypress/support/commands.js | 3 +- .../editorComponents/DraggableComponent.tsx | 5 - .../src/pages/Editor/GlobalHotKeys.test.tsx | 1 - .../src/pages/Editor/MainContainer.test.tsx | 555 ++++++++++++++++++ app/client/src/pages/Editor/WidgetCard.tsx | 67 +-- .../reducers/uiReducers/dragResizeReducer.ts | 1 + .../hooks/useBlocksToBeDraggedOnCanvas.ts | 19 +- .../src/utils/hooks/useCanvasDragToScroll.ts | 1 - .../src/utils/hooks/useCanvasDragging.ts | 54 +- .../test/factories/WidgetFactoryUtils.ts | 1 + app/client/test/sagas.ts | 2 + app/client/test/setup.ts | 2 + 12 files changed, 612 insertions(+), 99 deletions(-) create mode 100644 app/client/src/pages/Editor/MainContainer.test.tsx diff --git a/app/client/cypress/support/commands.js b/app/client/cypress/support/commands.js index c6dbb9ec012a..71e1fa60a32e 100644 --- a/app/client/cypress/support/commands.js +++ b/app/client/cypress/support/commands.js @@ -3,7 +3,6 @@ /* eslint-disable cypress/no-assigning-return-values */ require("cypress-file-upload"); -require("@4tw/cypress-drag-drop"); const loginPage = require("../locators/LoginPage.json"); const homePage = require("../locators/HomePage.json"); @@ -2076,7 +2075,7 @@ Cypress.Commands.add("runAndDeleteQuery", () => { Cypress.Commands.add("dragAndDropToCanvas", (widgetType, { x, y }) => { const selector = `.t--widget-card-draggable-${widgetType}`; cy.get(selector) - .trigger("mousedown", { button: 0 }, { force: true }) + .trigger("dragstart", { force: true }) .trigger("mousemove", x, y, { force: true }); cy.get(explorer.dropHere) .trigger("mousemove", x, y, { eventConstructor: "MouseEvent" }) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 947a6e89e7f5..e315fadf7628 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -12,7 +12,6 @@ import { import { commentModeSelector } from "selectors/commentsSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { selectWidgetInitAction } from "actions/widgetSelectionActions"; -import { useEffect } from "react"; const DraggableWrapper = styled.div` display: block; @@ -145,11 +144,7 @@ function DraggableComponent(props: DraggableComponentProps) { ); const className = `${classNameForTesting}`; const dispatch = useDispatch(); - const mightBeDragging = useRef(false); const draggableRef = useRef(null); - useEffect(() => { - mightBeDragging.current = false; - }, [isResizing]); const onDragStart = (e: any) => { e.preventDefault(); diff --git a/app/client/src/pages/Editor/GlobalHotKeys.test.tsx b/app/client/src/pages/Editor/GlobalHotKeys.test.tsx index d41450dd8e79..3e548a9b9086 100644 --- a/app/client/src/pages/Editor/GlobalHotKeys.test.tsx +++ b/app/client/src/pages/Editor/GlobalHotKeys.test.tsx @@ -23,7 +23,6 @@ import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; describe("Select all hotkey", () => { const mockGetIsFetchingPage = jest.spyOn(utilities, "getIsFetchingPage"); const spyGetCanvasWidgetDsl = jest.spyOn(utilities, "getCanvasWidgetDsl"); - Element.prototype.scrollIntoView = jest.fn(); function UpdatedMainContainer({ dsl }: any) { useMockDsl(dsl); diff --git a/app/client/src/pages/Editor/MainContainer.test.tsx b/app/client/src/pages/Editor/MainContainer.test.tsx new file mode 100644 index 000000000000..d7d1b77ca4d5 --- /dev/null +++ b/app/client/src/pages/Editor/MainContainer.test.tsx @@ -0,0 +1,555 @@ +import React from "react"; +import { + buildChildren, + widgetCanvasFactory, +} from "test/factories/WidgetFactoryUtils"; +import { act, render, fireEvent } from "test/testUtils"; +import GlobalHotKeys from "./GlobalHotKeys"; +import MainContainer from "./MainContainer"; +import { MemoryRouter } from "react-router-dom"; +import * as utilities from "selectors/editorSelectors"; +import store from "store"; +import { sagasToRunForTests } from "test/sagas"; +import { all } from "@redux-saga/core/effects"; +import { + MockApplication, + mockGetCanvasWidgetDsl, + syntheticTestMouseEvent, + useMockDsl, +} from "test/testCommon"; +import lodash from "lodash"; +import { getAbsolutePixels } from "utils/helpers"; +describe("Drag and Drop widgets into Main container", () => { + const mockGetIsFetchingPage = jest.spyOn(utilities, "getIsFetchingPage"); + const spyGetCanvasWidgetDsl = jest.spyOn(utilities, "getCanvasWidgetDsl"); + + function UpdatedMainContainer({ dsl }: any) { + useMockDsl(dsl); + return ; + } + // These need to be at the top to avoid imports not being mocked. ideally should be in setup.ts but will override for all other tests + beforeAll(() => { + const mockGenerator = function*() { + yield all([]); + }; + const debounceMocked = jest.spyOn(lodash, "debounce"); + debounceMocked.mockImplementation((fn: any) => fn); + + // top avoid the first middleware run which wud initiate all sagas. + jest.mock("sagas", () => ({ + rootSaga: mockGenerator, + })); + + // only the deafault exports are mocked to avoid overriding utilities exported out of them. defaults are marked to avoid worker initiation and page api calls in tests. + jest.mock("sagas/EvaluationsSaga", () => ({ + ...jest.requireActual("sagas/EvaluationsSaga"), + default: mockGenerator, + })); + jest.mock("sagas/PageSagas", () => ({ + ...jest.requireActual("sagas/PageSagas"), + default: mockGenerator, + })); + }); + + it("Drag to move widgets", () => { + const children: any = buildChildren([ + { + type: "TABS_WIDGET", + topRow: 5, + bottomRow: 5, + leftColumn: 5, + rightColumn: 5, + }, + ]); + const dsl: any = widgetCanvasFactory.build({ + children, + }); + spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); + mockGetIsFetchingPage.mockImplementation(() => false); + + const component = render( + + + + + + + , + { initialState: store.getState(), sagasToRun: sagasToRunForTests }, + ); + const propPane = component.queryByTestId("t--propertypane"); + expect(propPane).toBeNull(); + const canvasWidgets = component.queryAllByTestId("test-widget"); + expect(canvasWidgets.length).toBe(1); + const tabsWidget: any = component.container.querySelector( + ".t--draggable-tabswidget", + ); + const tab: any = component.container.querySelector(".t--widget-tabswidget"); + const initPositions = { + left: tab.style.left, + top: tab.style.top, + }; + act(() => { + fireEvent.dragStart(tabsWidget); + }); + + const mainCanvas: any = component.queryByTestId("canvas-dragging-0"); + act(() => { + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: 0, + offsetY: 0, + }, + ), + ); + }); + act(() => { + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: -50, + offsetY: -50, + }, + ), + ); + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mouseup", { + bubbles: true, + cancelable: true, + }), + ), + ); + jest.runAllTimers(); + }); + const movedTab: any = component.container.querySelector( + ".t--widget-tabswidget", + ); + const finalPositions = { + left: movedTab.style.left, + top: movedTab.style.top, + }; + expect(finalPositions.left).not.toEqual(initPositions.left); + expect(finalPositions.top).not.toEqual(initPositions.top); + }); + + it("When widgets are moved out of main container bounds move them back to previous position", () => { + const children: any = buildChildren([ + { + type: "TABS_WIDGET", + topRow: 5, + bottomRow: 5, + leftColumn: 5, + rightColumn: 5, + }, + ]); + const dsl: any = widgetCanvasFactory.build({ + children, + }); + spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); + mockGetIsFetchingPage.mockImplementation(() => false); + + const component = render( + + + + + + + , + { initialState: store.getState(), sagasToRun: sagasToRunForTests }, + ); + const propPane = component.queryByTestId("t--propertypane"); + expect(propPane).toBeNull(); + const canvasWidgets = component.queryAllByTestId("test-widget"); + expect(canvasWidgets.length).toBe(1); + const tabsWidget: any = component.container.querySelector( + ".t--draggable-tabswidget", + ); + const tab: any = component.container.querySelector(".t--widget-tabswidget"); + const initPositions = { + left: tab.style.left, + top: tab.style.top, + }; + act(() => { + fireEvent.dragStart(tabsWidget); + }); + + const mainCanvas: any = component.queryByTestId("canvas-dragging-0"); + act(() => { + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: 0, + offsetY: 0, + }, + ), + ); + }); + act(() => { + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: -500, + offsetY: -500, + }, + ), + ); + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mouseup", { + bubbles: true, + cancelable: true, + }), + ), + ); + jest.runAllTimers(); + }); + const movedTab: any = component.container.querySelector( + ".t--widget-tabswidget", + ); + const finalPositions = { + left: movedTab.style.left, + top: movedTab.style.top, + }; + expect(finalPositions.left).toEqual(initPositions.left); + expect(finalPositions.top).toEqual(initPositions.top); + }); + + it("When widgets are colliding with other widgets move them back to previous position", () => { + const children: any = buildChildren([ + { + type: "TABS_WIDGET", + topRow: 5, + bottomRow: 15, + leftColumn: 5, + rightColumn: 15, + }, + { + type: "TABLE_WIDGET", + topRow: 15, + bottomRow: 25, + leftColumn: 5, + rightColumn: 15, + }, + ]); + const dsl: any = widgetCanvasFactory.build({ + children, + }); + spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); + mockGetIsFetchingPage.mockImplementation(() => false); + + const component = render( + + + + + + + , + { initialState: store.getState(), sagasToRun: sagasToRunForTests }, + ); + const propPane = component.queryByTestId("t--propertypane"); + expect(propPane).toBeNull(); + const canvasWidgets = component.queryAllByTestId("test-widget"); + expect(canvasWidgets.length).toBe(2); + const tabsWidget: any = component.container.querySelector( + ".t--draggable-tabswidget", + ); + const tab: any = component.container.querySelector(".t--widget-tabswidget"); + const initPositions = { + left: tab.style.left, + top: tab.style.top, + }; + act(() => { + fireEvent.dragStart(tabsWidget); + }); + + const mainCanvas: any = component.queryByTestId("canvas-dragging-0"); + act(() => { + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: 0, + offsetY: 0, + }, + ), + ); + }); + act(() => { + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: 0, + offsetY: 50, + }, + ), + ); + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mouseup", { + bubbles: true, + cancelable: true, + }), + ), + ); + jest.runAllTimers(); + }); + const movedTab: any = component.container.querySelector( + ".t--widget-tabswidget", + ); + const finalPositions = { + left: movedTab.style.left, + top: movedTab.style.top, + }; + expect(finalPositions.left).toEqual(initPositions.left); + expect(finalPositions.top).toEqual(initPositions.top); + }); + + it("When widgets are out of bottom most bounds of parent canvas, canvas has to expand", () => { + const children: any = buildChildren([ + { + type: "TABS_WIDGET", + topRow: 5, + bottomRow: 15, + leftColumn: 5, + rightColumn: 15, + }, + { + type: "TABLE_WIDGET", + topRow: 15, + bottomRow: 25, + leftColumn: 5, + rightColumn: 15, + }, + ]); + const dsl: any = widgetCanvasFactory.build({ + children, + }); + dsl.bottomRow = 250; + spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); + mockGetIsFetchingPage.mockImplementation(() => false); + + const component = render( + + + + + + + , + { initialState: store.getState(), sagasToRun: sagasToRunForTests }, + ); + const propPane = component.queryByTestId("t--propertypane"); + expect(propPane).toBeNull(); + const canvasWidgets = component.queryAllByTestId("test-widget"); + expect(canvasWidgets.length).toBe(2); + + const tabsWidget: any = component.container.querySelector( + ".t--draggable-tablewidget", + ); + + act(() => { + fireEvent.dragStart(tabsWidget); + // jest.runAllTimers(); + }); + + const mainCanvas: any = component.queryByTestId("canvas-dragging-0"); + const dropTarget: any = component.container.getElementsByClassName( + "t--drop-target", + )[0]; + let initialLength = dropTarget.style.height; + act(() => { + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: 0, + offsetY: 0, + }, + ), + ); + }); + act(() => { + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: 0, + offsetY: 300, + // min height - (component height + bottom row) + }, + ), + ); + jest.runAllTimers(); + }); + let updatedDropTarget: any = component.container.getElementsByClassName( + "t--drop-target", + )[0]; + let updatedLength = updatedDropTarget.style.height; + + expect(initialLength).not.toEqual(updatedLength); + initialLength = updatedLength; + const amountMovedY = 300; + act(() => { + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: 0, + offsetY: 300 + amountMovedY, + }, + ), + ); + jest.runAllTimers(); + }); + updatedDropTarget = component.container.getElementsByClassName( + "t--drop-target", + )[0]; + updatedLength = updatedDropTarget.style.height; + expect(getAbsolutePixels(initialLength) + amountMovedY).toEqual( + getAbsolutePixels(updatedLength), + ); + }); + + it("Drag and Drop widget into an empty canvas", () => { + const children: any = buildChildren([]); + const dsl: any = widgetCanvasFactory.build({ + children, + }); + spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); + mockGetIsFetchingPage.mockImplementation(() => false); + + const component = render( + + + + + + + , + { initialState: store.getState(), sagasToRun: sagasToRunForTests }, + ); + const propPane = component.queryByTestId("t--propertypane"); + expect(propPane).toBeNull(); + const canvasWidgets = component.queryAllByTestId("test-widget"); + // empty canvas + expect(canvasWidgets.length).toBe(0); + const allAddEntityButtons: any = component.queryAllByText("+"); + const widgetAddButton = allAddEntityButtons[1]; + act(() => { + fireEvent.click(widgetAddButton); + jest.runAllTimers(); + }); + const containerButton: any = component.queryByText("Container"); + + act(() => { + fireEvent.dragStart(containerButton); + }); + + const mainCanvas: any = component.queryByTestId("canvas-dragging-0"); + act(() => { + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: 200, + offsetY: 200, + }, + ), + ); + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: 200, + offsetY: 200, + }, + ), + ); + fireEvent( + mainCanvas, + syntheticTestMouseEvent( + new MouseEvent("mouseup", { + bubbles: true, + cancelable: true, + }), + { + offsetX: 200, + offsetY: 200, + }, + ), + ); + }); + const newlyAddedCanvas = component.container.querySelectorAll( + "div[type='CONTAINER_WIDGET']", + ); + expect(newlyAddedCanvas.length).toBe(1); + }); + + afterAll(() => jest.resetModules()); +}); diff --git a/app/client/src/pages/Editor/WidgetCard.tsx b/app/client/src/pages/Editor/WidgetCard.tsx index afb58a4593ee..7c629b412907 100644 --- a/app/client/src/pages/Editor/WidgetCard.tsx +++ b/app/client/src/pages/Editor/WidgetCard.tsx @@ -1,13 +1,8 @@ -import React, { useState } from "react"; -import { useDrag, DragPreviewImage } from "react-dnd"; -import blankImage from "assets/images/blank.png"; +import React from "react"; import { WidgetCardProps } from "widgets/BaseWidget"; import styled from "styled-components"; import { WidgetIcons } from "icons/WidgetIcons"; -import { - useShowPropertyPane, - useWidgetDragResize, -} from "utils/hooks/dragResizeHooks"; +import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { generateReactKey } from "utils/generators"; import { Colors } from "constants/Colors"; @@ -77,36 +72,21 @@ export const IconLabel = styled.h5` function WidgetCard(props: CardProps) { const { setDraggingNewWidget } = useWidgetDragResize(); const { deselectAll } = useWidgetSelection(); - const showPropertyPane = useShowPropertyPane(); - const { selectWidget } = useWidgetSelection(); - // Generate a new widgetId which can be used in the future for this widget. - const [widgetId, setWidgetId] = useState(generateReactKey()); - const [, drag, preview] = useDrag({ - item: { ...props.details, widgetId }, - begin: () => { - AnalyticsUtil.logEvent("WIDGET_CARD_DRAG", { - widgetType: props.details.type, - widgetName: props.details.widgetCardName, + const onDragStart = (e: any) => { + e.preventDefault(); + e.stopPropagation(); + deselectAll(); + AnalyticsUtil.logEvent("WIDGET_CARD_DRAG", { + widgetType: props.details.type, + widgetName: props.details.widgetCardName, + }); + setDraggingNewWidget && + setDraggingNewWidget(true, { + ...props.details, + widgetId: generateReactKey(), }); - setDraggingNewWidget && - setDraggingNewWidget(true, { ...props.details, widgetId }); - deselectAll(); - }, - end: (widget: any, monitor) => { - AnalyticsUtil.logEvent("WIDGET_CARD_DROP", { - widgetType: props.details.type, - widgetName: props.details.widgetCardName, - didDrop: monitor.didDrop(), - }); - // We've finished dragging, generate a new widgetId to be used for next drag. - setWidgetId(generateReactKey()); - setDraggingNewWidget && setDraggingNewWidget(false, undefined); - selectWidget(widget.widgetId); - showPropertyPane(widget.widgetId); - }, - }); - + }; const iconType: string = props.details.type; const Icon = WidgetIcons[iconType]; const className = `t--widget-card-draggable-${props.details.type @@ -114,16 +94,13 @@ function WidgetCard(props: CardProps) { .join("") .toLowerCase()}`; return ( - <> - - -
- - {props.details.widgetCardName} - {props.details.isBeta && Beta} -
-
- + +
+ + {props.details.widgetCardName} + {props.details.isBeta && Beta} +
+
); } diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index 0ebe32b93afa..b8d8c49168a4 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -54,6 +54,7 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { state.isDragging = action.payload.isDragging; state.dragDetails = { newWidget: action.payload.newWidgetProps, + draggedOn: MAIN_CONTAINER_WIDGET_ID, }; }, [ReduxActionTypes.SET_WIDGET_RESIZING]: ( diff --git a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts index 7dabab6b2df0..dfed12ceef6c 100644 --- a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -22,6 +22,9 @@ import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena"; import { useDispatch } from "react-redux"; import { ReduxActionTypes } from "constants/ReduxActionConstants"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; +import { useShowPropertyPane } from "./dragResizeHooks"; +import { useWidgetSelection } from "./useWidgetSelection"; +import AnalyticsUtil from "utils/AnalyticsUtil"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { updateWidgetParams: WidgetOperationParams; @@ -47,6 +50,8 @@ export const useBlocksToBeDraggedOnCanvas = ({ widgetId, }: CanvasDraggingArenaProps) => { const dispatch = useDispatch(); + const showPropertyPane = useShowPropertyPane(); + const { selectWidget } = useWidgetSelection(); const containerPadding = noPad ? 0 : CONTAINER_GRID_PADDING; const dragDetails = useSelector(getDragDetails); const defaultHandlePositions = { @@ -197,6 +202,13 @@ export const useBlocksToBeDraggedOnCanvas = ({ updateWidgetParams.widgetId, updateWidgetParams.payload, ); + selectWidget(updateWidgetParams.payload.newWidgetId); + showPropertyPane(updateWidgetParams.payload.newWidgetId); + AnalyticsUtil.logEvent("WIDGET_CARD_DRAG", { + widgetType: dragDetails.newWidget.type, + widgetName: dragDetails.newWidget.widgetCardName, + didDrop: true, + }); }; const updateRows = (drawingBlocks: WidgetDraggingBlock[], rows: number) => { if (drawingBlocks.length) { @@ -224,6 +236,9 @@ export const useBlocksToBeDraggedOnCanvas = ({ }, [snapRows, isDragging]); const isChildOfCanvas = dragParent === widgetId; + const isCurrentDraggedCanvas = dragDetails.draggedOn === widgetId; + const isNewWidgetInitialTargetCanvas = + isNewWidget && widgetId === MAIN_CONTAINER_WIDGET_ID; const parentDiff = isDragging ? { top: @@ -255,11 +270,10 @@ export const useBlocksToBeDraggedOnCanvas = ({ 2 * containerPadding, } : defaultHandlePositions; - const currentOccSpaces = occupiedSpaces[widgetId]; + const currentOccSpaces = occupiedSpaces[widgetId] || []; const occSpaces: OccupiedSpace[] = isChildOfCanvas ? filteredChildOccupiedSpaces : currentOccSpaces; - const isCurrentDraggedCanvas = dragDetails.draggedOn === widgetId; return { blocksToDraw, defaultHandlePositions, @@ -267,6 +281,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ isCurrentDraggedCanvas, isDragging, isNewWidget, + isNewWidgetInitialTargetCanvas, isResizing, occSpaces, onDrop, diff --git a/app/client/src/utils/hooks/useCanvasDragToScroll.ts b/app/client/src/utils/hooks/useCanvasDragToScroll.ts index e1b0e01dc73c..a97cd742bfe4 100644 --- a/app/client/src/utils/hooks/useCanvasDragToScroll.ts +++ b/app/client/src/utils/hooks/useCanvasDragToScroll.ts @@ -42,7 +42,6 @@ export const useCanvasDragToScroll = ( (scrollByPixels < 0 && scrollParent.scrollTop > 0) || scrollByPixels > 0 ) { - console.count("scrollFn"); scrollParent.scrollBy({ top: scrollByPixels, behavior: "smooth", diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index 1aed974eb59a..269422cce832 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -37,6 +37,7 @@ export const useCanvasDragging = ( isCurrentDraggedCanvas, isDragging, isNewWidget, + isNewWidgetInitialTargetCanvas, isResizing, occSpaces, onDrop, @@ -52,6 +53,7 @@ export const useCanvasDragging = ( snapRowSpace, widgetId, }); + const { setDraggingCanvas, setDraggingNewWidget, @@ -118,6 +120,7 @@ export const useCanvasDragging = ( startPoints.top = defaultHandlePositions.top; startPoints.left = defaultHandlePositions.left; resetCanvasState(); + if (isCurrentDraggedCanvas) { if (isNewWidget) { setDraggingNewWidget(false, undefined); @@ -136,8 +139,10 @@ export const useCanvasDragging = ( canvasRef.current ) { if (!isNewWidget) { - startPoints.left = relativeStartPoints.left; - startPoints.top = relativeStartPoints.top; + startPoints.left = + relativeStartPoints.left || defaultHandlePositions.left; + startPoints.top = + relativeStartPoints.top || defaultHandlePositions.top; } if (!isCurrentDraggedCanvas) { // we can just use canvasIsDragging but this is needed to render the relative DragLayerComponent @@ -241,40 +246,8 @@ export const useCanvasDragging = ( } }); - const debouncedFn = debounce( - () => { - console.count("debouncedFn"); - if (scrollParent) { - const { - lastMouseMoveEvent, - lastScrollHeight, - lastScrollTop, - } = scrollObj; - const delta = - scrollParent?.scrollHeight + - scrollParent?.scrollTop - - (lastScrollHeight + lastScrollTop); - if (delta) { - scrollParent.scrollBy({ top: delta, behavior: "smooth" }); - } - onMouseMove({ - offsetX: lastMouseMoveEvent.offsetX, - offsetY: lastMouseMoveEvent.offsetY + delta, - }); - canScroll.current = true; - } - }, - 50, - { - leading: false, - trailing: true, - }, - ); - const endRenderRows = throttle( () => { - canScroll.current = false; - debouncedFn.cancel(); canScroll.current = true; }, 50, @@ -377,9 +350,6 @@ export const useCanvasDragging = ( scrollParent?.scrollHeight + scrollParent?.scrollTop - (lastScrollHeight + lastScrollTop); - if (delta) { - console.count("onScroll"); - } onMouseMove({ offsetX: lastMouseMoveEvent.offsetX, offsetY: lastMouseMoveEvent.offsetY + delta, @@ -420,12 +390,10 @@ export const useCanvasDragging = ( canvasCtx.scale(scale, scale); updateCanvasStyles(); initializeListeners(); - if (canvasIsDragging) { - blocksToDraw.forEach((each) => { - drawBlockOnCanvas(each); - }); - } - if (isChildOfCanvas && canvasRef.current) { + if ( + (isChildOfCanvas || isNewWidgetInitialTargetCanvas) && + canvasRef.current + ) { canvasRef.current.style.zIndex = "2"; } } diff --git a/app/client/test/factories/WidgetFactoryUtils.ts b/app/client/test/factories/WidgetFactoryUtils.ts index 7200938d476c..b30500752473 100644 --- a/app/client/test/factories/WidgetFactoryUtils.ts +++ b/app/client/test/factories/WidgetFactoryUtils.ts @@ -5,6 +5,7 @@ import defaultTemplate from "../../src/templates/default"; import { WidgetTypeFactories } from "./Widgets/WidgetTypeFactories"; const defaultMainContainer: ContainerWidgetProps = { ...(defaultTemplate as any), + canExtend: true, renderMode: "PAGE", version: 1, isLoading: false, diff --git a/app/client/test/sagas.ts b/app/client/test/sagas.ts index e44156e80e71..2b6f9adc72a7 100644 --- a/app/client/test/sagas.ts +++ b/app/client/test/sagas.ts @@ -27,6 +27,7 @@ import { watchDatasourcesSagas } from "../src/sagas/DatasourcesSagas"; import tourSagas from "../src/sagas/TourSagas"; import notificationsSagas from "../src/sagas/NotificationsSagas"; import selectionCanvasSagas from "../src/sagas/SelectionCanvasSagas"; +import draggingCanvasSagas from "../src/sagas/DraggingCanvasSagas"; export const sagasToRunForTests = [ initSagas, @@ -58,4 +59,5 @@ export const sagasToRunForTests = [ tourSagas, notificationsSagas, selectionCanvasSagas, + draggingCanvasSagas, ]; diff --git a/app/client/test/setup.ts b/app/client/test/setup.ts index 467389f24877..8154e1f86d53 100644 --- a/app/client/test/setup.ts +++ b/app/client/test/setup.ts @@ -3,6 +3,8 @@ import { handlers } from "./__mocks__/apiHandlers"; export const server = setupServer(...handlers); window.scrollTo = jest.fn(); +Element.prototype.scrollIntoView = jest.fn(); +Element.prototype.scrollBy = jest.fn(); // establish API mocking before all tests beforeAll(() => server.listen()); From c7a4863c88f87df50cde8956143db37a8cd567fe Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 23 Jul 2021 12:12:29 +0530 Subject: [PATCH 43/68] show border and disable resize when multiple widgets are selected. --- .../editorComponents/ResizableComponent.tsx | 3 +- .../ResizeStyledComponents.tsx | 63 ++++++++++++------- .../WidgetNameComponent/index.tsx | 11 ++-- app/client/src/constants/Colors.tsx | 1 + app/client/src/constants/DefaultTheme.tsx | 1 + .../src/pages/common/CanvasSelectionArena.tsx | 2 +- app/client/src/resizable/index.tsx | 18 ++++-- 7 files changed, 67 insertions(+), 32 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index e1cd68e9d4aa..08520048b834 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -292,9 +292,10 @@ export const ResizableComponent = memo(function ResizableComponent( const isEnabled = !isDragging && isWidgetFocused && !props.resizeDisabled && !isCommentMode; - + const allowResize = selectedWidgets && selectedWidgets.length <= 1; return ( ` + position: absolute; + width: ${EDGE_RESIZE_HANDLE_WIDTH}px; + height: ${EDGE_RESIZE_HANDLE_WIDTH}px; + &::before { + position: absolute; + background: ${(props) => + props.showAsBorder + ? theme.colors.widgetMultiSelectBorder + : theme.colors.widgetBorder}; + content: ""; + } + ${(props) => (!props.showAsBorder ? ResizeIndicatorStyle : "")} +`; + +export const VerticalHandleStyles = css<{ + showAsBorder: boolean; +}>` ${EdgeHandleStyles} top:-${WIDGET_PADDING - 1}px; height: calc(100% + ${2 * WIDGET_PADDING - 1}px); - cursor: col-resize; + ${(props) => (!props.showAsBorder ? "cursor: col-resize;" : "")} &:before { left: 50%; bottom: 0px; @@ -48,11 +59,13 @@ export const VerticalHandleStyles = css` } `; -export const HorizontalHandleStyles = css` +export const HorizontalHandleStyles = css<{ + showAsBorder: boolean; +}>` ${EdgeHandleStyles} left: -${WIDGET_PADDING}px; width: calc(100% + ${2 * WIDGET_PADDING}px); - cursor: row-resize; + ${(props) => (!props.showAsBorder ? "cursor: row-resize;" : "")} &:before { top: 50%; right: 0px; @@ -89,28 +102,36 @@ export const CornerHandleStyles = css` height: ${CORNER_RESIZE_HANDLE_WIDTH}px; `; -export const BottomRightHandleStyles = styled.div` +export const BottomRightHandleStyles = styled.div<{ + showAsBorder: boolean; +}>` ${CornerHandleStyles}; bottom: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; right: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - cursor: se-resize; + ${(props) => (!props.showAsBorder ? "cursor: se-resize;" : "")} `; -export const BottomLeftHandleStyles = styled.div` +export const BottomLeftHandleStyles = styled.div<{ + showAsBorder: boolean; +}>` ${CornerHandleStyles}; left: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; bottom: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - cursor: sw-resize; + ${(props) => (!props.showAsBorder ? "cursor: sw-resize;" : "")} `; -export const TopLeftHandleStyles = styled.div` +export const TopLeftHandleStyles = styled.div<{ + showAsBorder: boolean; +}>` ${CornerHandleStyles}; left: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; top: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - cursor: ew-resize; + ${(props) => (!props.showAsBorder ? "cursor: ew-resize;" : "")} `; -export const TopRightHandleStyles = styled.div` +export const TopRightHandleStyles = styled.div<{ + showAsBorder: boolean; +}>` ${CornerHandleStyles}; right: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; top: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - cursor: ne-resize; + ${(props) => (!props.showAsBorder ? "cursor: ne-resize;" : "")} `; diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index 32a561ba3659..620a624d839d 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -111,11 +111,12 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { selectedWidgets.includes(props.widgetId); const showWidgetName = - props.showControls || - ((focusedWidget === props.widgetId || showAsSelected) && - !isDragging && - !isResizing) || - !!props.errorCount; + selectedWidgets.length <= 1 && + (props.showControls || + ((focusedWidget === props.widgetId || showAsSelected) && + !isDragging && + !isResizing) || + !!props.errorCount); let currentActivity = props.type === WidgetTypes.MODAL_WIDGET diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index e84c959cde14..fe2517be72ab 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -81,6 +81,7 @@ export const Colors: Record = { OPAQ_BLUE: "rgba(106, 134, 206, 0.1)", RATE_ACTIVE: "#FFCB45", RATE_INACTIVE: "#F2F2F2", + MALIBU: "#7DBCFF", }; export type Color = typeof Colors[keyof typeof Colors]; diff --git a/app/client/src/constants/DefaultTheme.tsx b/app/client/src/constants/DefaultTheme.tsx index 0996d2b8a2c4..f1d2c150bf67 100644 --- a/app/client/src/constants/DefaultTheme.tsx +++ b/app/client/src/constants/DefaultTheme.tsx @@ -2366,6 +2366,7 @@ export const theme: Theme = { menuIconColorInactive: Colors.OXFORD_BLUE, bodyBG: Colors.ATHENS_GRAY, builderBodyBG: Colors.WHITE, + widgetMultiSelectBorder: Colors.MALIBU, widgetBorder: Colors.SLATE_GRAY, widgetSecondaryBorder: Colors.MERCURY, messageBG: Colors.CONCRETE, diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 9b4c991f299b..421f0446bf68 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -180,7 +180,7 @@ export function CanvasSelectionArena({ const drawRectangle = (selectionDimensions: SelectedArenaDimensions) => { const strokeWidth = 1; canvasCtx.setLineDash([5]); - canvasCtx.strokeStyle = "rgb(84, 132, 236)"; + canvasCtx.strokeStyle = "rgba(125,188,255,1)"; canvasCtx.strokeRect( selectionDimensions.left - strokeWidth, selectionDimensions.top - strokeWidth, diff --git a/app/client/src/resizable/index.tsx b/app/client/src/resizable/index.tsx index 0647b0e5dd7f..22edb22cc581 100644 --- a/app/client/src/resizable/index.tsx +++ b/app/client/src/resizable/index.tsx @@ -6,11 +6,11 @@ import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; -const ResizeWrapper = styled.div<{ pevents: boolean }>` +const ResizeWrapper = styled.div<{ prevents: boolean }>` display: block; & { * { - pointer-events: ${(props) => !props.pevents && "none"}; + pointer-events: ${(props) => !props.prevents && "none"}; } } `; @@ -27,6 +27,7 @@ const getSnappedValues = ( }; type ResizableHandleProps = { + allowResize: boolean; dragCallback: (x: number, y: number) => void; component: StyledComponent<"div", Record>; onStart: () => void; @@ -40,6 +41,9 @@ type ResizableHandleProps = { function ResizableHandle(props: ResizableHandleProps) { const bind = useDrag( ({ first, last, dragging, movement: [mx, my], memo }) => { + if (!props.allowResize) { + return; + } const snapped = getSnappedValues(mx, my, props.snapGrid); if (dragging && memo && (snapped.x !== memo.x || snapped.y !== memo.y)) { props.dragCallback(snapped.x, snapped.y); @@ -53,11 +57,16 @@ function ResizableHandle(props: ResizableHandleProps) { return snapped; }, ); + const propsToPass = { + ...bind(), + showAsBorder: !props.allowResize, + }; - return ; + return ; } type ResizableProps = { + allowResize: boolean; handles: { left?: StyledComponent<"div", Record>; top?: StyledComponent<"div", Record>; @@ -271,6 +280,7 @@ export const Resizable = forwardRef(function Resizable( const renderHandles = handles.map((handle, index) => ( { togglePointerEvents(false); @@ -302,7 +312,7 @@ export const Resizable = forwardRef(function Resizable( {(_props) => ( From 4ec2d480165d191e37ee64ba334c143ef63b6caa Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 23 Jul 2021 12:19:44 +0530 Subject: [PATCH 44/68] selection box grab cursor --- app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx index 33b5b4aa98b4..90f420adc490 100644 --- a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx +++ b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx @@ -26,6 +26,7 @@ import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; const StyledSelectionBox = styled.div` position: absolute; + cursor: grab; `; const StyledActionsContainer = styled.div` From 89cd819273ab647ea81a36befb71b9c5ea17813a Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 23 Jul 2021 12:46:49 +0530 Subject: [PATCH 45/68] merge conflicts. --- .../editorComponents/DropTargetComponent.tsx | 10 +++++----- .../editorComponents/ResizableComponent.tsx | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 8dc524de2223..ebb40c6191fc 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -9,7 +9,6 @@ import React, { useMemo, } from "react"; import styled from "styled-components"; -import { useDrop, XYCoord, DropTargetMonitor } from "react-dnd"; import { isEqual } from "lodash"; import { WidgetProps } from "widgets/BaseWidget"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; @@ -154,15 +153,16 @@ export function DropTargetComponent(props: DropTargetComponentProps) { props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1, occupiedSpacesByChildren, ); - if (rows < newRows) { - setRows(newRows); - return true; + if (rowRef.current < newRows) { + rowRef.current = newRows; + updateHeight(); + return newRows; } return false; } return false; }, - [props.minHeight, occupiedSpacesByChildren, canDropTargetExtend, rows], + [props.minHeight, occupiedSpacesByChildren, canDropTargetExtend], ); const handleFocus = (e: any) => { diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 4c32fbe43a21..08520048b834 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -38,6 +38,7 @@ import { import AnalyticsUtil from "utils/AnalyticsUtil"; import { scrollElementIntoParentCanvasView } from "utils/helpers"; import { getNearestParentCanvas } from "utils/generators"; +import { getOccupiedSpaces } from "selectors/editorSelectors"; import { commentModeSelector } from "selectors/commentsSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; @@ -51,12 +52,11 @@ export const ResizableComponent = memo(function ResizableComponent( const resizableRef = useRef(null); // Fetch information from the context const { updateWidget } = useContext(EditorContext); + const occupiedSpaces = useSelector(getOccupiedSpaces); - const { - occupiedSpaces: occupiedSpacesBySiblingWidgets, - persistDropTargetRows, - updateDropTargetRows, - } = useContext(DropTargetContext); + const { persistDropTargetRows, updateDropTargetRows } = useContext( + DropTargetContext, + ); const isCommentMode = useSelector(commentModeSelector); @@ -81,6 +81,11 @@ export const ResizableComponent = memo(function ResizableComponent( (state: AppState) => state.ui.widgetDragResize.isResizing, ); + const occupiedSpacesBySiblingWidgets = + occupiedSpaces && props.parentId && occupiedSpaces[props.parentId] + ? occupiedSpaces[props.parentId] + : undefined; + // isFocused (string | boolean) -> isWidgetFocused (boolean) const isWidgetFocused = focusedWidget === props.widgetId || @@ -313,7 +318,4 @@ export const ResizableComponent = memo(function ResizableComponent( ); }); - -ResizableComponent.displayName = "ResizableComponent"; - export default ResizableComponent; From 58afaea5316efb043ad9c9265f3cc8be2e5f0df8 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 23 Jul 2021 12:50:24 +0530 Subject: [PATCH 46/68] code clean up --- .../editorComponents/DropTargetComponent.tsx | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index ebb40c6191fc..72c6ca3aeab4 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -26,7 +26,6 @@ import { } from "utils/hooks/dragResizeHooks"; import { getOccupiedSpacesSelectorForContainer } from "selectors/editorSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; -import { debounce } from "lodash"; type DropTargetComponentProps = WidgetProps & { children?: ReactNode; @@ -129,20 +128,14 @@ export function DropTargetComponent(props: DropTargetComponentProps) { [props.minHeight, props.widgetId, canDropTargetExtend], ); - const updateHeight = useCallback( - debounce(() => { - if (dropTargetRef.current) { - const height = canDropTargetExtend - ? `${Math.max( - rowRef.current * props.snapRowSpace, - props.minHeight, - )}px` - : "100%"; - dropTargetRef.current.style.height = height; - } - }), - [canDropTargetExtend], - ); + const updateHeight = useCallback(() => { + if (dropTargetRef.current) { + const height = canDropTargetExtend + ? `${Math.max(rowRef.current * props.snapRowSpace, props.minHeight)}px` + : "100%"; + dropTargetRef.current.style.height = height; + } + }, [canDropTargetExtend]); /* Update the rows of the main container based on the current widget's (dragging/resizing) bottom row */ const updateDropTargetRows = useCallback( (widgetId: string, widgetBottomRow: number) => { From a058dab218342b856f4bc5caeadf9e78c3175f8a Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 23 Jul 2021 15:08:01 +0530 Subject: [PATCH 47/68] fixing specs. --- .../editorComponents/DraggableComponent.tsx | 14 +++--- .../src/pages/Editor/GlobalHotKeys.test.tsx | 43 ++++++------------- .../pages/Editor/WidgetsMultiSelectBox.tsx | 1 + .../common/CanvasSelectionArena.test.tsx | 4 +- 4 files changed, 21 insertions(+), 41 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index e315fadf7628..0db612c8215b 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -78,6 +78,7 @@ function DraggableComponent(props: DraggableComponentProps) { const focusedWidget = useSelector( (state: AppState) => state.ui.widgetDragResize.focusedWidget, ); + const isCurrentWidgetSelected = selectedWidgets.includes(props.widgetId); // This state tells us whether a `ResizableComponent` is resizing const isResizing = useSelector( @@ -98,10 +99,8 @@ function DraggableComponent(props: DraggableComponentProps) { // True when any widget is dragging or resizing, including this one const isResizingOrDragging = !!isResizing || !!isDragging; - const isCurrentWidgetDragging = - isDragging && selectedWidgets.includes(props.widgetId); - const isCurrentWidgetResizing = - isResizing && selectedWidgets.includes(props.widgetId); + const isCurrentWidgetDragging = isDragging && isCurrentWidgetSelected; + const isCurrentWidgetResizing = isResizing && isCurrentWidgetSelected; // When mouse is over this draggable const handleMouseOver = (e: any) => { focusWidget && @@ -110,9 +109,7 @@ function DraggableComponent(props: DraggableComponentProps) { focusWidget(props.widgetId); e.stopPropagation(); }; - const shouldRenderComponent = !( - selectedWidgets.includes(props.widgetId) && isDragging - ); + const shouldRenderComponent = !(isCurrentWidgetSelected && isDragging); // Display this draggable based on the current drag state const style: CSSProperties = { display: isCurrentWidgetDragging ? "none" : "block", @@ -150,7 +147,7 @@ function DraggableComponent(props: DraggableComponentProps) { e.preventDefault(); e.stopPropagation(); if (draggableRef.current && !e.metaKey) { - if (!selectedWidgets.includes(props.widgetId)) { + if (!isCurrentWidgetSelected) { dispatch(selectWidgetInitAction(props.widgetId)); } const bounds = draggableRef.current.getBoundingClientRect(); @@ -172,6 +169,7 @@ function DraggableComponent(props: DraggableComponentProps) { return ( { })); }); - it("Cmd + A - select all widgets on canvas", () => { + it("Cmd + A - select all widgets on canvas", async () => { const children: any = buildChildren([ { type: "TABS_WIDGET" }, { type: "SWITCH_WIDGET" }, @@ -96,9 +96,7 @@ describe("Select all hotkey", () => { false, true, ); - let selectedWidgets = component.queryAllByTestId( - "t--widget-propertypane-toggle", - ); + let selectedWidgets = component.queryAllByTestId("t--selected"); expect(selectedWidgets.length).toBe(2); dispatchTestKeyboardEventWithCode( component.container, @@ -108,9 +106,7 @@ describe("Select all hotkey", () => { false, false, ); - selectedWidgets = component.queryAllByTestId( - "t--widget-propertypane-toggle", - ); + selectedWidgets = component.queryAllByTestId("t--selected"); expect(selectedWidgets.length).toBe(0); act(() => { dispatchTestKeyboardEventWithCode( @@ -123,9 +119,7 @@ describe("Select all hotkey", () => { ); }); - selectedWidgets = component.queryAllByTestId( - "t--widget-propertypane-toggle", - ); + selectedWidgets = component.queryAllByTestId("t--selected"); expect(selectedWidgets.length).toBe(2); act(() => { dispatchTestKeyboardEventWithCode( @@ -147,9 +141,7 @@ describe("Select all hotkey", () => { true, ); }); - selectedWidgets = component.queryAllByTestId( - "t--widget-propertypane-toggle", - ); + selectedWidgets = component.queryAllByTestId("t--selected"); expect(selectedWidgets.length).toBe(2); }); afterAll(() => jest.resetModules()); @@ -197,9 +189,7 @@ describe("Cut/Copy/Paste hotkey", () => { ); }); - let selectedWidgets = await component.queryAllByTestId( - "t--widget-propertypane-toggle", - ); + let selectedWidgets = await component.queryAllByTestId("t--selected"); expect(selectedWidgets.length).toBe(2); act(() => { dispatchTestKeyboardEventWithCode( @@ -221,7 +211,8 @@ describe("Cut/Copy/Paste hotkey", () => { true, ); }); - await component.findByText(children[0].widgetName + "Copy"); + await component.findByTestId("t--selection-box"); + act(() => { dispatchTestKeyboardEventWithCode( component.container, @@ -233,9 +224,7 @@ describe("Cut/Copy/Paste hotkey", () => { ); }); - selectedWidgets = await component.queryAllByTestId( - "t--widget-propertypane-toggle", - ); + selectedWidgets = await component.queryAllByTestId("t--selected"); expect(selectedWidgets.length).toBe(4); }); it("Should cut and paste all selected widgets with hotkey cmd + x and cmd + v ", async () => { @@ -281,9 +270,7 @@ describe("Cut/Copy/Paste hotkey", () => { ); }); - let selectedWidgets = await component.queryAllByTestId( - "t--widget-propertypane-toggle", - ); + let selectedWidgets = await component.queryAllByTestId("t--selected"); expect(selectedWidgets.length).toBe(2); act(() => { dispatchTestKeyboardEventWithCode( @@ -296,9 +283,7 @@ describe("Cut/Copy/Paste hotkey", () => { ); }); await component.findByTestId("canvas-0"); - selectedWidgets = await component.queryAllByTestId( - "t--widget-propertypane-toggle", - ); + selectedWidgets = await component.queryAllByTestId("t--selected"); //adding extra time to let cut cmd works jest.useFakeTimers(); setTimeout(() => { @@ -315,7 +300,7 @@ describe("Cut/Copy/Paste hotkey", () => { true, ); }); - await component.findByText(children[0].widgetName); + await component.findByTestId("t--selection-box"); act(() => { dispatchTestKeyboardEventWithCode( component.container, @@ -327,9 +312,7 @@ describe("Cut/Copy/Paste hotkey", () => { ); }); - selectedWidgets = await component.queryAllByTestId( - "t--widget-propertypane-toggle", - ); + selectedWidgets = await component.queryAllByTestId("t--selected"); expect(selectedWidgets.length).toBe(2); }); }); diff --git a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx index 90f420adc490..beea785e0c19 100644 --- a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx +++ b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx @@ -305,6 +305,7 @@ function WidgetsMultiSelectBox(props: { return ( { }, ), ); - const selectedWidgets = component.queryAllByTestId( - "t--widget-propertypane-toggle", - ); + const selectedWidgets = component.queryAllByTestId("t--selected"); expect(selectedWidgets.length).toBe(2); }); }); From bbf0d83f3f6918da262b220b9590af64244084ac Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 23 Jul 2021 16:53:00 +0530 Subject: [PATCH 48/68] fixed a bug and failed specs. --- .../WidgetSelection/WidgetSelection_spec.js | 8 +++----- .../components/editorComponents/ResizableComponent.tsx | 8 ++++++-- .../editorComponents/WidgetNameComponent/index.tsx | 6 +++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/WidgetSelection/WidgetSelection_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/WidgetSelection/WidgetSelection_spec.js index 54ff3d797ddc..3a4f1fd6ac6a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/WidgetSelection/WidgetSelection_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/WidgetSelection/WidgetSelection_spec.js @@ -12,17 +12,15 @@ describe("Widget Selection", function() { cy.get(`#${dsl.dsl.children[1].widgetId}`).click({ ctrlKey: true, }); - cy.get(`.t--widget-propertypane-toggle`).should("have.length", 2); + cy.get(`div[data-testid='t--selected']`).should("have.length", 2); cy.get(`#${dsl.dsl.children[2].widgetId}`).click({ ctrlKey: true, }); - cy.get(`.t--widget-propertypane-toggle`).should("have.length", 3); + cy.get(`div[data-testid='t--selected']`).should("have.length", 3); cy.get(`#${dsl.dsl.children[0].widgetId}`).click({ ctrlKey: true, }); - cy.get(`.t--widget-propertypane-toggle`) - .not(`[style *= "background: rgb(255, 224, 210);"]`) // Excluding focused widgets. - .should("have.length", 2); + cy.get(`div[data-testid='t--selected']`).should("have.length", 2); cy.get(`.t--multi-selection-box`).should("have.length", 1); }); diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 08520048b834..7bc05cbebe76 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -292,10 +292,14 @@ export const ResizableComponent = memo(function ResizableComponent( const isEnabled = !isDragging && isWidgetFocused && !props.resizeDisabled && !isCommentMode; - const allowResize = selectedWidgets && selectedWidgets.length <= 1; + const isMultiSelectedWidget = + selectedWidgets && + selectedWidgets.length > 1 && + selectedWidgets.includes(props.widgetId); + return ( 1 && + selectedWidgets.includes(props.widgetId); const showWidgetName = - selectedWidgets.length <= 1 && + !isMultiSelectedWidget && (props.showControls || ((focusedWidget === props.widgetId || showAsSelected) && !isDragging && From 6d97fc1c3eab89cef752f1bce6d3e3b1baa64563 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 26 Jul 2021 10:24:07 +0530 Subject: [PATCH 49/68] fixing rerenders. --- .../editorComponents/DropTargetComponent.tsx | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 72c6ca3aeab4..f86ffd497b16 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -107,26 +107,23 @@ export function DropTargetComponent(props: DropTargetComponentProps) { } }, [props.bottomRow, props.canExtend]); - const persistDropTargetRows = useCallback( - (widgetId: string, widgetBottomRow: number) => { - const newRows = calculateDropTargetRows( - widgetId, - widgetBottomRow, - props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1, - occupiedSpacesByChildren, - ); - const rowsToPersist = Math.max( - props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1, - newRows, - ); - rowRef.current = rowsToPersist; - updateHeight(); - if (canDropTargetExtend) { - updateCanvasSnapRows(props.widgetId, rowsToPersist); - } - }, - [props.minHeight, props.widgetId, canDropTargetExtend], - ); + const persistDropTargetRows = (widgetId: string, widgetBottomRow: number) => { + const newRows = calculateDropTargetRows( + widgetId, + widgetBottomRow, + props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1, + occupiedSpacesByChildren, + ); + const rowsToPersist = Math.max( + props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1, + newRows, + ); + rowRef.current = rowsToPersist; + updateHeight(); + if (canDropTargetExtend) { + updateCanvasSnapRows(props.widgetId, rowsToPersist); + } + }; const updateHeight = useCallback(() => { if (dropTargetRef.current) { @@ -136,27 +133,23 @@ export function DropTargetComponent(props: DropTargetComponentProps) { dropTargetRef.current.style.height = height; } }, [canDropTargetExtend]); - /* Update the rows of the main container based on the current widget's (dragging/resizing) bottom row */ - const updateDropTargetRows = useCallback( - (widgetId: string, widgetBottomRow: number) => { - if (canDropTargetExtend) { - const newRows = calculateDropTargetRows( - widgetId, - widgetBottomRow, - props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1, - occupiedSpacesByChildren, - ); - if (rowRef.current < newRows) { - rowRef.current = newRows; - updateHeight(); - return newRows; - } - return false; + const updateDropTargetRows = (widgetId: string, widgetBottomRow: number) => { + if (canDropTargetExtend) { + const newRows = calculateDropTargetRows( + widgetId, + widgetBottomRow, + props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1, + occupiedSpacesByChildren, + ); + if (rowRef.current < newRows) { + rowRef.current = newRows; + updateHeight(); + return newRows; } return false; - }, - [props.minHeight, occupiedSpacesByChildren, canDropTargetExtend], - ); + } + return false; + }; const handleFocus = (e: any) => { if (!isResizing && !isDragging) { From 1399c2fb296c7ab91c987aeb41beaf33996335a0 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 26 Jul 2021 11:10:30 +0530 Subject: [PATCH 50/68] code clean up --- .../editorComponents/DraggableComponent.tsx | 2 +- .../components/editorComponents/Dropzone.tsx | 153 ------------------ .../hooks/useBlocksToBeDraggedOnCanvas.ts | 20 +++ .../src/utils/hooks/useCanvasDragging.ts | 2 +- 4 files changed, 22 insertions(+), 155 deletions(-) delete mode 100644 app/client/src/components/editorComponents/Dropzone.tsx diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 0db612c8215b..6441623b11f3 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -146,7 +146,7 @@ function DraggableComponent(props: DraggableComponentProps) { const onDragStart = (e: any) => { e.preventDefault(); e.stopPropagation(); - if (draggableRef.current && !e.metaKey) { + if (draggableRef.current && !(e.metaKey || e.ctrlKey)) { if (!isCurrentWidgetSelected) { dispatch(selectWidgetInitAction(props.widgetId)); } diff --git a/app/client/src/components/editorComponents/Dropzone.tsx b/app/client/src/components/editorComponents/Dropzone.tsx deleted file mode 100644 index 16ea1a168a83..000000000000 --- a/app/client/src/components/editorComponents/Dropzone.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import React, { useEffect, forwardRef, Ref } from "react"; -import { XYCoord } from "react-dnd"; -import styled from "styled-components"; -import { snapToGrid } from "utils/helpers"; -import { IntentColors } from "constants/DefaultTheme"; -import { useSpring, animated, interpolate, config } from "react-spring"; -import { Layers } from "constants/Layers"; - -const SPRING_CONFIG = { - ...config.gentle, - clamp: true, - friction: 0, - tension: 999, -}; -const DropZoneWrapper = styled.div<{ - width: number; - height: number; - candrop: boolean; -}>` - width: ${(props) => props.width}px; - height: ${[(props) => props.height]}px; - position: absolute; - background: ${(props) => (props.candrop ? IntentColors.success : "#333")}; - ${(props) => - !props.candrop - ? ` - background-image: linear-gradient(45deg, #EB2121 8.33%, #33322A 8.33%, #33322A 50%, #EB2121 50%, #EB2121 58.33%, #33322A 58.33%, #33322A 100%); - background-size: 16.97px 16.97px; - ` - : ""} - border: 1px dashed ${(props) => props.theme.colors.textAnchor}; - will-change: transform; - opacity: 0.6; - transition: visibility 0s 2s, opacity 2s linear; - z-index: 1; -`; - -const SnappedDropZoneWrapper = styled(DropZoneWrapper)<{ candrop: boolean }>` - background: ${(props) => - props.candrop ? props.theme.colors.hover : IntentColors.danger}; - z-index: 0; -`; - -const AnimatedDropZone = animated(DropZoneWrapper); -const AnimatedSnappingDropZone = animated(SnappedDropZoneWrapper); - -type DropZoneProps = { - currentOffset: XYCoord; - height: number; - width: number; - parentOffset: XYCoord; - parentRowHeight: number; - parentColumnWidth: number; - canDrop: boolean; -}; - -export const getSnappedXY = ( - parentColumnWidth: number, - parentRowHeight: number, - currentOffset: XYCoord, - parentOffset: XYCoord, -) => { - // TODO(abhinav): There is a simpler math to use. - const [leftColumn, topRow] = snapToGrid( - parentColumnWidth, - parentRowHeight, - currentOffset.x - parentOffset.x, - currentOffset.y - parentOffset.y, - ); - return { - X: leftColumn * parentColumnWidth, - Y: topRow * parentRowHeight, - }; -}; - -/* eslint-disable react/display-name */ - -export const DropZone = forwardRef( - (props: DropZoneProps, ref: Ref) => { - const [{ X, Y }, setXY] = useSpring<{ - X: number; - Y: number; - }>(() => ({ - X: props.currentOffset.x - props.parentOffset.x, - Y: props.currentOffset.y - props.parentOffset.y, - config: SPRING_CONFIG, - })); - - const [{ snappedX, snappedY }, setSnappedXY] = useSpring<{ - snappedX: number; - snappedY: number; - }>(() => ({ - snappedX: props.currentOffset.x - props.parentOffset.x, - snappedY: props.currentOffset.y - props.parentOffset.y, - config: SPRING_CONFIG, - })); - - useEffect(() => { - setXY({ - X: props.currentOffset.x - props.parentOffset.x, - Y: props.currentOffset.y - props.parentOffset.y, - }); - const snappedXY = getSnappedXY( - props.parentColumnWidth, - props.parentRowHeight, - props.currentOffset, - props.parentOffset, - ); - setSnappedXY({ - snappedX: snappedXY.X, - snappedY: snappedXY.Y, - }); - }, [ - props.parentColumnWidth, - props.parentRowHeight, - props.currentOffset, - props.parentOffset, - setSnappedXY, - setXY, - ]); - return ( - <> - `translate3d(${x}px,${y}px,0)`, - ), - }} - width={props.width * props.parentColumnWidth} - /> - `translate3d(${x}px,${y}px,0)`, - ), - }} - width={props.width * props.parentColumnWidth} - /> - - ); - }, -); - -export default DropZone; diff --git a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts index dfed12ceef6c..2d5f6b78a20a 100644 --- a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -25,6 +25,7 @@ import { EditorContext } from "components/editorComponents/EditorContextProvider import { useShowPropertyPane } from "./dragResizeHooks"; import { useWidgetSelection } from "./useWidgetSelection"; import AnalyticsUtil from "utils/AnalyticsUtil"; +import { snapToGrid } from "utils/helpers"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { updateWidgetParams: WidgetOperationParams; @@ -96,6 +97,24 @@ export const useBlocksToBeDraggedOnCanvas = ({ return {}; } }; + const getSnappedXY = ( + parentColumnWidth: number, + parentRowHeight: number, + currentOffset: XYCoord, + parentOffset: XYCoord, + ) => { + // TODO(abhinav): There is a simpler math to use. + const [leftColumn, topRow] = snapToGrid( + parentColumnWidth, + parentRowHeight, + currentOffset.x - parentOffset.x, + currentOffset.y - parentOffset.y, + ); + return { + X: leftColumn * parentColumnWidth, + Y: topRow * parentRowHeight, + }; + }; const getBlocksToDraw = (): WidgetDraggingBlock[] => { if (isNewWidget) { return [ @@ -277,6 +296,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ return { blocksToDraw, defaultHandlePositions, + getSnappedXY, isChildOfCanvas, isCurrentDraggedCanvas, isDragging, diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index 269422cce832..07063cf3b81d 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -1,4 +1,3 @@ -import { getSnappedXY } from "components/editorComponents/Dropzone"; import { CONTAINER_GRID_PADDING, GridDefaults, @@ -33,6 +32,7 @@ export const useCanvasDragging = ( const { blocksToDraw, defaultHandlePositions, + getSnappedXY, isChildOfCanvas, isCurrentDraggedCanvas, isDragging, From 6da59b0662cc356f0586e55441355b62793bd0c5 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 28 Jul 2021 11:40:18 +0530 Subject: [PATCH 51/68] code review comments --- .../editorComponents/DraggableComponent.tsx | 49 +++++++++---------- .../editorComponents/DropTargetComponent.tsx | 14 ++++-- .../editorComponents/ResizableComponent.tsx | 5 +- .../pages/Editor/WidgetsMultiSelectBox.tsx | 11 ++--- .../src/pages/common/CanvasSelectionArena.tsx | 3 -- app/client/src/sagas/DraggingCanvasSagas.ts | 3 +- .../src/utils/hooks/dragResizeHooks.tsx | 13 +++-- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 6 +++ .../src/utils/hooks/useCanvasDragging.ts | 4 +- 9 files changed, 59 insertions(+), 49 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 6441623b11f3..99975e7a9638 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -1,8 +1,8 @@ -import React, { CSSProperties, useRef } from "react"; +import React, { CSSProperties, useMemo, useRef } from "react"; import styled from "styled-components"; import { WidgetProps } from "widgets/BaseWidget"; import { WIDGET_PADDING } from "constants/WidgetConstants"; -import { useDispatch, useSelector } from "react-redux"; +import { useSelector } from "react-redux"; import { AppState } from "reducers"; import { getColorWithOpacity } from "constants/DefaultTheme"; import { @@ -11,7 +11,6 @@ import { } from "utils/hooks/dragResizeHooks"; import { commentModeSelector } from "selectors/commentsSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; -import { selectWidgetInitAction } from "actions/widgetSelectionActions"; const DraggableWrapper = styled.div` display: block; @@ -62,7 +61,7 @@ export const canDrag = ( function DraggableComponent(props: DraggableComponentProps) { // Dispatch hook handy to set a widget as focused/selected - const { focusWidget } = useWidgetSelection(); + const { focusWidget, selectWidget } = useWidgetSelection(); const isCommentMode = useSelector(commentModeSelector); @@ -111,22 +110,20 @@ function DraggableComponent(props: DraggableComponentProps) { }; const shouldRenderComponent = !(isCurrentWidgetSelected && isDragging); // Display this draggable based on the current drag state - const style: CSSProperties = { + const dragWrapperStyle: CSSProperties = { display: isCurrentWidgetDragging ? "none" : "block", }; - - // WidgetBoundaries - const widgetBoundaries = ( - - ); + const dragBoundariesStyle: React.CSSProperties = useMemo(() => { + return { + opacity: !isResizingOrDragging || isCurrentWidgetResizing ? 0 : 1, + position: "absolute", + transform: `translate(-50%, -50%)`, + top: "50%", + left: "50%", + }; + }, [isResizingOrDragging, isCurrentWidgetResizing]); + + const widgetBoundaries = ; const classNameForTesting = `t--draggable-${props.type .split("_") @@ -140,7 +137,6 @@ function DraggableComponent(props: DraggableComponentProps) { isCommentMode, ); const className = `${classNameForTesting}`; - const dispatch = useDispatch(); const draggableRef = useRef(null); const onDragStart = (e: any) => { @@ -148,7 +144,7 @@ function DraggableComponent(props: DraggableComponentProps) { e.stopPropagation(); if (draggableRef.current && !(e.metaKey || e.ctrlKey)) { if (!isCurrentWidgetSelected) { - dispatch(selectWidgetInitAction(props.widgetId)); + selectWidget(props.widgetId); } const bounds = draggableRef.current.getBoundingClientRect(); const startPoints = { @@ -157,12 +153,13 @@ function DraggableComponent(props: DraggableComponentProps) { }; showTableFilterPane(); setDraggingCanvas(props.parentId); - setDraggingState( - true, - props.parentId || "", - { widgetId: props.widgetId }, + + setDraggingState({ + isDragging: true, + dragGroupActualParent: props.parentId || "", + draggingGroupCenter: { widgetId: props.widgetId }, startPoints, - ); + }); } }; @@ -174,7 +171,7 @@ function DraggableComponent(props: DraggableComponentProps) { onDragStart={onDragStart} onMouseOver={handleMouseOver} ref={draggableRef} - style={style} + style={dragWrapperStyle} > {shouldRenderComponent && props.children} {widgetBoundaries} diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index f86ffd497b16..86e2e6f9fc5f 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -26,6 +26,7 @@ import { } from "utils/hooks/dragResizeHooks"; import { getOccupiedSpacesSelectorForContainer } from "selectors/editorSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { getDragDetails } from "sagas/selectors"; type DropTargetComponentProps = WidgetProps & { children?: ReactNode; @@ -75,9 +76,12 @@ export function DropTargetComponent(props: DropTargetComponentProps) { (state: AppState) => state.ui.widgetDragResize.isDragging, ); - const dragDetails = useSelector( - (state: AppState) => state.ui.widgetDragResize.dragDetails, - ); + // dragDetails contains of info needed for a container jump: + // which parent the dragging widget belongs, + // which canvas is active(being dragged on), + // which widget is grabbed while dragging started, + // relative position of mouse pointer wrt to the last grabbed widget. + const dragDetails = useSelector(getDragDetails); const { draggedOn } = dragDetails; @@ -125,14 +129,14 @@ export function DropTargetComponent(props: DropTargetComponentProps) { } }; - const updateHeight = useCallback(() => { + const updateHeight = () => { if (dropTargetRef.current) { const height = canDropTargetExtend ? `${Math.max(rowRef.current * props.snapRowSpace, props.minHeight)}px` : "100%"; dropTargetRef.current.style.height = height; } - }, [canDropTargetExtend]); + }; const updateDropTargetRows = (widgetId: string, widgetBottomRow: number) => { if (canDropTargetExtend) { const newRows = calculateDropTargetRows( diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 7bc05cbebe76..57e0fbc60f32 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -81,10 +81,11 @@ export const ResizableComponent = memo(function ResizableComponent( (state: AppState) => state.ui.widgetDragResize.isResizing, ); - const occupiedSpacesBySiblingWidgets = - occupiedSpaces && props.parentId && occupiedSpaces[props.parentId] + const occupiedSpacesBySiblingWidgets = useMemo(() => { + return occupiedSpaces && props.parentId && occupiedSpaces[props.parentId] ? occupiedSpaces[props.parentId] : undefined; + }, [occupiedSpaces, props.parentId]); // isFocused (string | boolean) -> isWidgetFocused (boolean) const isWidgetFocused = diff --git a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx index beea785e0c19..d939f868a816 100644 --- a/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx +++ b/app/client/src/pages/Editor/WidgetsMultiSelectBox.tsx @@ -212,16 +212,15 @@ function WidgetsMultiSelectBox(props: { const top = minBy(selectedWidgets, (rect) => rect.topRow)?.topRow; const left = minBy(selectedWidgets, (rect) => rect.leftColumn) ?.leftColumn; - - setDraggingState( - true, - parentId || "", - { + setDraggingState({ + isDragging: true, + dragGroupActualParent: parentId || "", + draggingGroupCenter: { top, left, }, startPoints, - ); + }); } }; diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 421f0446bf68..b621c4c9ec9b 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -282,9 +282,6 @@ export function CanvasSelectionArena({ scrollParent?.scrollHeight + scrollParent?.scrollTop - (lastScrollHeight + lastScrollTop); - if (delta) { - console.count("onScroll"); - } onMouseMove({ offsetX: lastMouseMoveEvent.offsetX, offsetY: lastMouseMoveEvent.offsetY + delta, diff --git a/app/client/src/sagas/DraggingCanvasSagas.ts b/app/client/src/sagas/DraggingCanvasSagas.ts index 12d64aca5fee..da32185395e9 100644 --- a/app/client/src/sagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/DraggingCanvasSagas.ts @@ -74,8 +74,7 @@ function moveWidget(widgetMoveParams: WidgetMoveParams) { const stateWidget: FlattenedWidgetProps = allWidgets[widgetId]; let widget = Object.assign({}, stateWidget); // Get all widgets from DSL/Redux Store - const stateWidgets: CanvasWidgetsReduxState = cloneDeep(allWidgets); - const widgets = Object.assign({}, stateWidgets); + const widgets = Object.assign({}, allWidgets); // Get parent from DSL/Redux Store const stateParent: FlattenedWidgetProps = allWidgets[parentId]; const parent = { diff --git a/app/client/src/utils/hooks/dragResizeHooks.tsx b/app/client/src/utils/hooks/dragResizeHooks.tsx index 394b6ef087e0..3ee548481c97 100644 --- a/app/client/src/utils/hooks/dragResizeHooks.tsx +++ b/app/client/src/utils/hooks/dragResizeHooks.tsx @@ -110,12 +110,17 @@ export const useWidgetDragResize = () => { [dispatch], ), setDraggingState: useCallback( - ( - isDragging: boolean, + ({ + isDragging, dragGroupActualParent = "", draggingGroupCenter = {}, - startPoints?: any, - ) => { + startPoints, + }: { + isDragging: boolean; + dragGroupActualParent?: string; + draggingGroupCenter?: Record; + startPoints?: any; + }) => { if (isDragging) { document.body.classList.add("dragging"); } else { diff --git a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts index 2d5f6b78a20a..35ee375db914 100644 --- a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -54,6 +54,12 @@ export const useBlocksToBeDraggedOnCanvas = ({ const showPropertyPane = useShowPropertyPane(); const { selectWidget } = useWidgetSelection(); const containerPadding = noPad ? 0 : CONTAINER_GRID_PADDING; + + // dragDetails contains of info needed for a container jump: + // which parent the dragging widget belongs, + // which canvas is active(being dragged on), + // which widget is grabbed while dragging started, + // relative position of mouse pointer wrt to the last grabbed widget. const dragDetails = useSelector(getDragDetails); const defaultHandlePositions = { top: 20, diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index 07063cf3b81d..c429aee7dd88 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -125,7 +125,9 @@ export const useCanvasDragging = ( if (isNewWidget) { setDraggingNewWidget(false, undefined); } else { - setDraggingState(false); + setDraggingState({ + isDragging: false, + }); } setDraggingCanvas(); } From 3b0717a25e5bed3921b43e1b8fc3e7c974af2380 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 29 Jul 2021 11:35:32 +0530 Subject: [PATCH 52/68] always have the drag point inside the widget. --- .../editorComponents/DraggableComponent.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index ede14849a9d1..f2742d247ac3 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -151,10 +151,18 @@ function DraggableComponent(props: DraggableComponentProps) { if (!isCurrentWidgetSelected) { selectWidget(props.widgetId); } + const widgetHeight = props.bottomRow - props.topRow; + const widgetWidth = props.rightColumn - props.leftColumn; const bounds = draggableRef.current.getBoundingClientRect(); const startPoints = { - top: (e.clientY - bounds.top) / props.parentRowSpace, - left: (e.clientX - bounds.left) / props.parentColumnSpace, + top: Math.min( + Math.max((e.clientY - bounds.top) / props.parentRowSpace, 0), + widgetHeight - 1, + ), + left: Math.min( + Math.max((e.clientX - bounds.left) / props.parentColumnSpace, 0), + widgetWidth - 1, + ), }; showTableFilterPane(); setDraggingCanvas(props.parentId); From b35340696f1a6789a8c9667392f2f32eefe33ff9 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 30 Jul 2021 11:01:35 +0530 Subject: [PATCH 53/68] fetching snap spaces instead of calculating. --- .../src/actions/canvasSelectionActions.ts | 12 +++++- .../src/pages/common/CanvasSelectionArena.tsx | 6 +++ app/client/src/sagas/SelectionCanvasSagas.ts | 42 ++++++------------- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/app/client/src/actions/canvasSelectionActions.ts b/app/client/src/actions/canvasSelectionActions.ts index 9e76953b4fb4..8f59ecc967b7 100644 --- a/app/client/src/actions/canvasSelectionActions.ts +++ b/app/client/src/actions/canvasSelectionActions.ts @@ -20,9 +20,19 @@ export const selectAllWidgetsInAreaAction = ( snapToNextColumn: boolean, snapToNextRow: boolean, isMultiSelect: boolean, + snapSpaces: { + snapColumnSpace: number; + snapRowSpace: number; + }, ): ReduxAction => { return { type: ReduxActionTypes.SELECT_WIDGETS_IN_AREA, - payload: { selectionArena, snapToNextColumn, snapToNextRow, isMultiSelect }, + payload: { + selectionArena, + snapToNextColumn, + snapToNextRow, + isMultiSelect, + snapSpaces, + }, }; }; diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index b621c4c9ec9b..b8457173e86f 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -39,11 +39,13 @@ export interface SelectedArenaDimensions { export function CanvasSelectionArena({ canExtend, + snapColumnSpace, snapRows, snapRowSpace, widgetId, }: { canExtend: boolean; + snapColumnSpace: number; widgetId: string; snapRows: number; snapRowSpace: number; @@ -74,6 +76,10 @@ export function CanvasSelectionArena({ snapToNextColumn, snapToNextRow, isMultiSelect, + { + snapColumnSpace, + snapRowSpace, + }, ), ); }, diff --git a/app/client/src/sagas/SelectionCanvasSagas.ts b/app/client/src/sagas/SelectionCanvasSagas.ts index af30d5d5e24d..0c85ce236eeb 100644 --- a/app/client/src/sagas/SelectionCanvasSagas.ts +++ b/app/client/src/sagas/SelectionCanvasSagas.ts @@ -1,12 +1,6 @@ import { selectMultipleWidgetsAction } from "actions/widgetSelectionActions"; import { OccupiedSpace } from "constants/editorConstants"; import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; -import { - CONTAINER_GRID_PADDING, - WIDGET_PADDING, - GridDefaults, - MAIN_CONTAINER_WIDGET_ID, -} from "constants/WidgetConstants"; import { isEqual } from "lodash"; import { SelectedArenaDimensions } from "pages/common/CanvasSelectionArena"; import { all, cancel, put, select, take, takeLatest } from "redux-saga/effects"; @@ -38,6 +32,7 @@ function* selectAllWidgetsInAreaSaga( const { isMultiSelect, selectionArena, + snapSpaces, snapToNextColumn, snapToNextRow, }: { @@ -45,38 +40,25 @@ function* selectAllWidgetsInAreaSaga( snapToNextColumn: boolean; snapToNextRow: boolean; isMultiSelect: boolean; + snapSpaces: { + snapColumnSpace: number; + snapRowSpace: number; + }; } = action.payload; - let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; - if ( - mainContainer.widgetId === MAIN_CONTAINER_WIDGET_ID || - mainContainer.type === "CONTAINER_WIDGET" - ) { - //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. - padding = CONTAINER_GRID_PADDING * 2; - } - if (mainContainer.noPad) { - // Widgets like ListWidget choose to have no container padding so will only have widget padding - padding = WIDGET_PADDING * 2; - } - // const padding = CONTAINER_GRID_PADDING + WIDGET_PADDING; - const snapSpace = { - snapColumnWidth: - (mainContainer.rightColumn - padding) / GridDefaults.DEFAULT_GRID_COLUMNS, - snapColumnHeight: GridDefaults.DEFAULT_GRID_ROW_HEIGHT, - }; + const { snapColumnSpace, snapRowSpace } = snapSpaces; // we use snapToNextRow, snapToNextColumn to determine if the selection rectangle is inverted // so to snap not to the next column or row like we usually do, // but to snap it to the one before it coz the rectangle is inverted. const topLeftCorner = snapToGrid( - snapSpace.snapColumnWidth, - snapSpace.snapColumnHeight, - selectionArena.left - (snapToNextRow ? snapSpace.snapColumnWidth : 0), - selectionArena.top - (snapToNextColumn ? snapSpace.snapColumnHeight : 0), + snapColumnSpace, + snapRowSpace, + selectionArena.left - (snapToNextRow ? snapColumnSpace : 0), + selectionArena.top - (snapToNextColumn ? snapRowSpace : 0), ); const bottomRightCorner = snapToGrid( - snapSpace.snapColumnWidth, - snapSpace.snapColumnHeight, + snapColumnSpace, + snapRowSpace, selectionArena.left + selectionArena.width, selectionArena.top + selectionArena.height, ); From 64b1c810cb400284a47fa41ecfa4660419655076 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 3 Aug 2021 15:59:12 +0530 Subject: [PATCH 54/68] remove widget_move action --- app/client/src/constants/ReduxActionConstants.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index c26d73754a7f..2d43482d49a7 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -630,7 +630,6 @@ export const WidgetReduxActionTypes: { [key: string]: string } = { WIDGET_ADD_CHILD: "WIDGET_ADD_CHILD", WIDGET_CHILD_ADDED: "WIDGET_CHILD_ADDED", WIDGET_REMOVE_CHILD: "WIDGET_REMOVE_CHILD", - WIDGET_MOVE: "WIDGET_MOVE", WIDGET_RESIZE: "WIDGET_RESIZE", WIDGET_DELETE: "WIDGET_DELETE", WIDGET_BULK_DELETE: "WIDGET_BULK_DELETE", From 32e722d7336601adb7da5f71595c653edfaf251e Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 3 Aug 2021 17:17:08 +0530 Subject: [PATCH 55/68] fixing bugs with add widget parent height updation. --- app/client/src/sagas/WidgetOperationSagas.tsx | 28 +++++++++---------- app/client/src/sagas/WidgetOperationUtils.ts | 19 +++++++++++++ 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 77145bd169ab..604538a3f2ad 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -124,6 +124,7 @@ import LOG_TYPE from "entities/AppsmithConsole/logtype"; import { checkIfPastingIntoListWidget, doesTriggerPathsContainPropertyPath, + getParentBottomRowAfterAddingWidget, getParentWidgetIdForPasting, getWidgetChildren, handleSpecificCasesWhilePasting, @@ -308,16 +309,16 @@ export function* addChildSaga(addChildAction: ReduxAction) { ); const newWidget = childWidgetPayload.widgets[childWidgetPayload.widgetId]; - const updateBottomRow = - stateParent.type === WidgetTypes.CANVAS_WIDGET && - newWidget.bottomRow * newWidget.parentRowSpace > stateParent.bottomRow; + const parentBottomRow = getParentBottomRowAfterAddingWidget( + stateParent, + newWidget, + ); + // Update widgets to put back in the canvasWidgetsReducer // TODO(abhinav): This won't work if dont already have an empty children: [] const parent = { ...stateParent, - ...(updateBottomRow - ? { bottomRow: newWidget.bottomRow * newWidget.parentRowSpace } - : {}), + bottomRow: parentBottomRow, children: [...(stateParent.children || []), childWidgetPayload.widgetId], }; @@ -1628,18 +1629,16 @@ function* pasteWidgetSaga() { // Add the new child to existing children parentChildren = parentChildren.concat(widgetChildren); } - const updateBottomRow = - widget.bottomRow * widget.parentRowSpace > - widgets[pastingIntoWidgetId].bottomRow; + const parentBottomRow = getParentBottomRowAfterAddingWidget( + widgets[pastingIntoWidgetId], + widget, + ); + widgets = { ...widgets, [pastingIntoWidgetId]: { ...widgets[pastingIntoWidgetId], - ...(updateBottomRow - ? { - bottomRow: widget.bottomRow * widget.parentRowSpace, - } - : {}), + bottomRow: parentBottomRow, children: parentChildren, }, }; @@ -1793,6 +1792,7 @@ function* addSuggestedWidget(action: ReduxAction>) { topRow, rightColumn, bottomRow, + parentRowSpace: GridDefaults.DEFAULT_GRID_ROW_HEIGHT, }; yield put({ diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index bef4b8e497ad..94b96be60069 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -1,4 +1,5 @@ import { + GridDefaults, MAIN_CONTAINER_WIDGET_ID, WidgetTypes, } from "constants/WidgetConstants"; @@ -332,3 +333,21 @@ export const checkIfPastingIntoListWidget = function( } return selectedWidget; }; + +export const getParentBottomRowAfterAddingWidget = ( + stateParent: FlattenedWidgetProps, + newWidget: FlattenedWidgetProps, +) => { + const parentRowSpace = + newWidget.parentRowSpace || GridDefaults.DEFAULT_GRID_ROW_HEIGHT; + const updateBottomRow = + stateParent.type === WidgetTypes.CANVAS_WIDGET && + newWidget.bottomRow * parentRowSpace > stateParent.bottomRow; + return updateBottomRow + ? Math.max( + (newWidget.bottomRow + GridDefaults.CANVAS_EXTENSION_OFFSET) * + parentRowSpace, + stateParent.bottomRow, + ) + : stateParent.bottomRow; +}; From 88bbe5eb93761d3221ae64a383036f930fcc2d3e Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 6 Aug 2021 23:25:14 +0530 Subject: [PATCH 56/68] fixing specs. --- .../editorComponents/DropTargetComponent.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 1be13437463a..e52f31025a56 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -104,10 +104,12 @@ export function DropTargetComponent(props: DropTargetComponentProps) { useEffect(() => { const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); - rowRef.current = snapRows; - updateHeight(); - if (canDropTargetExtend) { - updateCanvasSnapRows(props.widgetId, snapRows); + if (rowRef.current !== snapRows) { + rowRef.current = snapRows; + updateHeight(); + if (canDropTargetExtend) { + updateCanvasSnapRows(props.widgetId, snapRows); + } } }, [props.bottomRow, props.canExtend]); From 7309f25ffb21c42e626ba5bd92a3dbd2c530b75e Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 9 Aug 2021 12:43:50 +0530 Subject: [PATCH 57/68] List widget conflict fixes. --- app/client/src/utils/hooks/usePositionedContainerZIndex.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/client/src/utils/hooks/usePositionedContainerZIndex.ts b/app/client/src/utils/hooks/usePositionedContainerZIndex.ts index 2cc7cec1d27d..b06bdc922760 100644 --- a/app/client/src/utils/hooks/usePositionedContainerZIndex.ts +++ b/app/client/src/utils/hooks/usePositionedContainerZIndex.ts @@ -29,7 +29,10 @@ export const usePositionedContainerZIndex = ( } } else { // common use case when nothing is dragged - return props.selected || props.focused + + return props.focused + ? Layers.focusedWidget + : props.selected ? Layers.selectedWidget : Layers.positionedWidget; } From d05cadd5b22768eed4f92a03ae7d76171a92c9f9 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 9 Aug 2021 13:15:21 +0530 Subject: [PATCH 58/68] fixing canvas drop persistence. --- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts index 35ee375db914..519ca58af56c 100644 --- a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -182,22 +182,39 @@ export const useBlocksToBeDraggedOnCanvas = ({ widget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : widgetId, ); - const widgetBottomRow = - updateWidgetParams.payload.topRow + - (updateWidgetParams.payload.rows || - widget.bottomRow - widget.topRow); - persistDropTargetRows && - persistDropTargetRows(widget.widgetId, widgetBottomRow); - return { ...each, updateWidgetParams, }; }); dispatchDrop(draggedBlocksToUpdate); + persistCanvasRows( + draggedBlocksToUpdate[draggedBlocksToUpdate.length - 1], + ); } }; + const persistCanvasRows = (bottomMostBlock: { + updateWidgetParams: WidgetOperationParams; + left: number; + top: number; + width: number; + height: number; + columnWidth: number; + rowHeight: number; + widgetId: string; + isNotColliding: boolean; + detachFromLayout?: boolean | undefined; + }) => { + const widget = newWidget ? newWidget : allWidgets[bottomMostBlock.widgetId]; + const { updateWidgetParams } = bottomMostBlock; + const widgetBottomRow = + updateWidgetParams.payload.topRow + + (updateWidgetParams.payload.rows || widget.bottomRow - widget.topRow); + persistDropTargetRows && + persistDropTargetRows(bottomMostBlock.widgetId, widgetBottomRow); + }; + const dispatchDrop = ( draggedBlocksToUpdate: WidgetDraggingUpdateParams[], ) => { From 28f289cf9ef15775a1f3bfbbae80fd5e6c8c4804 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 10 Aug 2021 11:34:20 +0530 Subject: [PATCH 59/68] Adding click to drag for modals and fixing few issues. --- .../editorComponents/DropTargetComponent.tsx | 1 + .../src/pages/common/CanvasSelectionArena.tsx | 4 +- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 6 ++- .../src/utils/hooks/useClickOpenPropPane.tsx | 52 +++++++++++++++++++ app/client/src/widgets/ContainerWidget.tsx | 1 + app/client/src/widgets/ModalWidget.tsx | 13 ++++- 6 files changed, 74 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index e52f31025a56..ee8b18f74a6f 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -42,6 +42,7 @@ const StyledDropTarget = styled.div` position: relative; background: none; user-select: none; + z-index: 1; `; function Onboarding() { diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index a4b557281b1a..64a1dec6c2bb 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -39,12 +39,14 @@ export interface SelectedArenaDimensions { export function CanvasSelectionArena({ canExtend, + draggableParent, snapColumnSpace, snapRows, snapRowSpace, widgetId, }: { canExtend: boolean; + draggableParent: boolean; snapColumnSpace: number; widgetId: string; snapRows: number; @@ -227,7 +229,7 @@ export function CanvasSelectionArena({ }; const onMouseDown = (e: any) => { - if (canvasRef.current && (widgetId === "0" || e.ctrlKey || e.metaKey)) { + if (canvasRef.current && (!draggableParent || e.ctrlKey || e.metaKey)) { isMultiSelect = e.ctrlKey || e.metaKey || e.shiftKey; selectionRectangle.left = e.offsetX - canvasRef.current.offsetLeft; selectionRectangle.top = e.offsetY - canvasRef.current.offsetTop; diff --git a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts index 519ca58af56c..310d017009e2 100644 --- a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -245,7 +245,11 @@ export const useBlocksToBeDraggedOnCanvas = ({ updateWidgetParams.payload, ); selectWidget(updateWidgetParams.payload.newWidgetId); - showPropertyPane(updateWidgetParams.payload.newWidgetId); + // Adding setTimeOut to allow property pane to open only after widget is loaded. + // Not needed for most widgets except for Modal Widget. + setTimeout(() => { + showPropertyPane(updateWidgetParams.payload.newWidgetId); + }, 100); AnalyticsUtil.logEvent("WIDGET_CARD_DRAG", { widgetType: dragDetails.newWidget.type, widgetName: dragDetails.newWidget.widgetCardName, diff --git a/app/client/src/utils/hooks/useClickOpenPropPane.tsx b/app/client/src/utils/hooks/useClickOpenPropPane.tsx index 150fb7d6cf07..bf20b2c49de2 100644 --- a/app/client/src/utils/hooks/useClickOpenPropPane.tsx +++ b/app/client/src/utils/hooks/useClickOpenPropPane.tsx @@ -13,6 +13,8 @@ import { APP_MODE } from "entities/App"; import { getAppMode } from "selectors/applicationSelectors"; import { getWidgets } from "sagas/selectors"; import { useWidgetSelection } from "./useWidgetSelection"; +import React, { ReactNode, useCallback } from "react"; +import { stopEventPropagation } from "utils/AppsmithUtils"; /** * @@ -48,6 +50,56 @@ export function getParentToOpenIfAny( return; } +export function ClickContentToOpenPropPane({ + children, + widgetId, +}: { + widgetId: string; + children?: ReactNode; +}) { + const { focusWidget } = useWidgetSelection(); + + const openPropertyPane = useClickOpenPropPane(); + const openPropPane = useCallback( + (e) => { + openPropertyPane(e, widgetId); + }, + [widgetId, openPropertyPane], + ); + const focusedWidget = useSelector( + (state: AppState) => state.ui.widgetDragResize.focusedWidget, + ); + + const isResizing = useSelector( + (state: AppState) => state.ui.widgetDragResize.isResizing, + ); + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); + const isResizingOrDragging = !!isResizing || !!isDragging; + const handleMouseOver = (e: any) => { + focusWidget && + !isResizingOrDragging && + focusedWidget !== widgetId && + focusWidget(widgetId); + e.stopPropagation(); + }; + + return ( +
+ {children} +
+ ); +} + export const useClickOpenPropPane = () => { const showPropertyPane = useShowPropertyPane(); const { focusWidget, selectWidget } = useWidgetSelection(); diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index e32b505de0d6..59d74d8aa1a0 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -140,6 +140,7 @@ class ContainerWidget extends BaseWidget< diff --git a/app/client/src/widgets/ModalWidget.tsx b/app/client/src/widgets/ModalWidget.tsx index 6fae51bd0823..cabfd9436f66 100644 --- a/app/client/src/widgets/ModalWidget.tsx +++ b/app/client/src/widgets/ModalWidget.tsx @@ -17,6 +17,7 @@ import * as Sentry from "@sentry/react"; import withMeta, { WithMeta } from "./MetaHOC"; import { AppState } from "reducers"; import { getWidget } from "sagas/selectors"; +import { ClickContentToOpenPropPane } from "utils/hooks/useClickOpenPropPane"; const MODAL_SIZE: { [id: string]: { width: number; height: number } } = { MODAL_SMALL: { @@ -105,7 +106,7 @@ export class ModalWidget extends BaseWidget { childWidgetData.shouldScrollContents = false; childWidgetData.canExtend = this.props.shouldScrollContents; childWidgetData.bottomRow = this.props.shouldScrollContents - ? childWidgetData.bottomRow + ? Math.max(childWidgetData.bottomRow, MODAL_SIZE[this.props.size].height) : MODAL_SIZE[this.props.size].height; childWidgetData.isVisible = this.props.isVisible; childWidgetData.containerStyle = "none"; @@ -142,6 +143,15 @@ export class ModalWidget extends BaseWidget { } } + makeModalSelectable(content: ReactNode): ReactNode { + // substitute coz the widget lacks draggable and position containers. + return ( + + {content} + + ); + } + makeModalComponent(content: ReactNode) { return ( { getCanvasView() { let children = this.getChildren(); + children = this.makeModalSelectable(children); children = this.showWidgetName(children, true); return this.makeModalComponent(children); } From 1785fe65a3b20f5776970d04721435cb9db1b0f5 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 13 Aug 2021 15:38:39 +0530 Subject: [PATCH 60/68] cmd + a and draw from outside --- .../src/actions/widgetSelectionActions.ts | 8 +- app/client/src/constants/WidgetConstants.tsx | 1 + app/client/src/pages/Editor/WidgetsEditor.tsx | 14 +- .../src/pages/common/CanvasSelectionArena.tsx | 45 +++++- .../uiReducers/canvasSelectionReducer.ts | 2 + app/client/src/sagas/WidgetSelectionSagas.ts | 141 +++++++++++++++--- 6 files changed, 177 insertions(+), 34 deletions(-) diff --git a/app/client/src/actions/widgetSelectionActions.ts b/app/client/src/actions/widgetSelectionActions.ts index c323a5312889..5bed6522f2ab 100644 --- a/app/client/src/actions/widgetSelectionActions.ts +++ b/app/client/src/actions/widgetSelectionActions.ts @@ -1,5 +1,4 @@ import { ReduxActionTypes, ReduxAction } from "constants/ReduxActionConstants"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; export const selectWidgetAction = ( widgetId?: string, @@ -44,14 +43,9 @@ export const deselectMultipleWidgetsAction = ( }; }; -export const selectAllWidgetsInCanvasInitAction = ( - canvasId = MAIN_CONTAINER_WIDGET_ID, -): ReduxAction<{ canvasId: string }> => { +export const selectAllWidgetsInCanvasInitAction = () => { return { type: ReduxActionTypes.SELECT_ALL_WIDGETS_IN_CANVAS_INIT, - payload: { - canvasId, - }, }; }; diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index ec3e5634339f..5967eb1a68ca 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -118,6 +118,7 @@ export const DroppableWidgets: WidgetType[] = [ WidgetTypes.FORM_WIDGET, WidgetTypes.LIST_WIDGET, WidgetTypes.TABS_WIDGET, + WidgetTypes.MODAL_WIDGET, ]; // Note: Widget Padding + Container Padding === DEFAULT_GRID_ROW_HEIGHT to gracefully lose one row when a container is used, diff --git a/app/client/src/pages/Editor/WidgetsEditor.tsx b/app/client/src/pages/Editor/WidgetsEditor.tsx index 23ce712da318..80867f1de1f1 100644 --- a/app/client/src/pages/Editor/WidgetsEditor.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor.tsx @@ -26,6 +26,8 @@ import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout"; import Debugger from "components/editorComponents/Debugger"; import { closePropertyPane, closeTableFilterPane } from "actions/widgetActions"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { setCanvasSelectionStateAction } from "actions/canvasSelectionActions"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; const EditorWrapper = styled.div` display: flex; @@ -103,6 +105,7 @@ function WidgetsEditor() { deselectAll && deselectAll(); dispatch(closePropertyPane()); dispatch(closeTableFilterPane()); + dispatch(setCanvasSelectionStateAction(false, MAIN_CONTAINER_WIDGET_ID)); }, [focusWidget, deselectAll]); const pageLoading = ( @@ -118,12 +121,21 @@ function WidgetsEditor() { if (!isFetchingPage && widgets) { node = ; } + const onDragStart = (e: any) => { + e.preventDefault(); + e.stopPropagation(); + dispatch(setCanvasSelectionStateAction(true, MAIN_CONTAINER_WIDGET_ID)); + }; log.debug("Canvas rendered"); PerformanceTracker.stopTracking(); return ( - + {node} diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 64a1dec6c2bb..f7232a63bf7d 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -3,7 +3,7 @@ import { setCanvasSelectionStateAction, } from "actions/canvasSelectionActions"; import { throttle } from "lodash"; -import React, { useEffect, useCallback } from "react"; +import React, { useEffect, useCallback, useRef } from "react"; import { useDispatch, useSelector } from "react-redux"; import { AppState } from "reducers"; import { APP_MODE } from "entities/App"; @@ -59,6 +59,9 @@ export function CanvasSelectionArena({ const isDragging = useSelector( (state: AppState) => state.ui.widgetDragResize.isDragging, ); + const isResizing = useSelector( + (state: AppState) => state.ui.widgetDragResize.isResizing, + ); const mainContainer = useSelector((state: AppState) => getWidget(state, widgetId), ); @@ -99,6 +102,10 @@ export function CanvasSelectionArena({ const isCurrentWidgetDrawing = useSelector((state: AppState) => { return state.ui.canvasSelection.widgetId === widgetId; }); + const drawOnEnter = useRef(false); + useEffect(() => { + drawOnEnter.current = isDraggingForSelection && isCurrentWidgetDrawing; + }, [isDraggingForSelection, isCurrentWidgetDrawing]); useCanvasDragToScroll( canvasRef, isCurrentWidgetDrawing, @@ -112,6 +119,7 @@ export function CanvasSelectionArena({ // as of today (Pixels rendered by canvas) ∝ (Application height) so as height increases will run into to dead renders. // https://on690.codesandbox.io/ to check the number of pixels limit supported for a canvas // const { devicePixelRatio: scale = 1 } = window; + const scale = 1; const scrollParent: Element | null = getNearestParentCanvas( canvasRef.current, @@ -209,7 +217,11 @@ export function CanvasSelectionArena({ document.body.addEventListener("click", onClick, false); }; - const onMouseEnter = () => { + const onMouseEnter = (e: any) => { + if (canvasRef.current && !isDragging && drawOnEnter.current) { + firstDraw(e); + drawOnEnter.current = false; + } document.body.removeEventListener("mouseup", onMouseUp); document.body.removeEventListener("click", onClick); }; @@ -228,19 +240,40 @@ export function CanvasSelectionArena({ } }; - const onMouseDown = (e: any) => { - if (canvasRef.current && (!draggableParent || e.ctrlKey || e.metaKey)) { + const determineDirection = (pos: any) => { + if (canvasRef.current) { + const bounds = canvasRef.current.getBoundingClientRect(); + const w = bounds.width, + h = bounds.height, + x = (pos.x - bounds.left - w / 2) * (w > h ? h / w : 1), + y = (pos.y - bounds.top - h / 2) * (h > w ? w / h : 1); + + return ( + Math.round((Math.atan2(y, x) * (180 / Math.PI) + 180) / 90 + 3) % 4 + ); + } + }; + + const firstDraw = (e: any) => { + if (canvasRef.current) { + console.log(determineDirection({ x: e.clientX, y: e.clientY })); isMultiSelect = e.ctrlKey || e.metaKey || e.shiftKey; selectionRectangle.left = e.offsetX - canvasRef.current.offsetLeft; selectionRectangle.top = e.offsetY - canvasRef.current.offsetTop; selectionRectangle.width = 0; selectionRectangle.height = 0; isDragging = true; - dispatch(setCanvasSelectionStateAction(true, widgetId)); // bring the canvas to the top layer canvasRef.current.style.zIndex = "2"; } }; + + const onMouseDown = (e: any) => { + if (canvasRef.current && (!draggableParent || e.ctrlKey || e.metaKey)) { + dispatch(setCanvasSelectionStateAction(true, widgetId)); + firstDraw(e); + } + }; const onMouseUp = () => { if (isDragging && canvasRef.current) { isDragging = false; @@ -310,7 +343,7 @@ export function CanvasSelectionArena({ } }, [appLayout, currentPageId, mainContainer, isDragging, snapRows]); - return appMode === APP_MODE.EDIT && !isDragging ? ( + return appMode === APP_MODE.EDIT && !(isDragging || isResizing) ? ( { state.isDraggingForSelection = false; + state.widgetId = ""; }, }); diff --git a/app/client/src/sagas/WidgetSelectionSagas.ts b/app/client/src/sagas/WidgetSelectionSagas.ts index 67fe87e16e10..0a4cd54b8acf 100644 --- a/app/client/src/sagas/WidgetSelectionSagas.ts +++ b/app/client/src/sagas/WidgetSelectionSagas.ts @@ -1,7 +1,15 @@ import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { all, put, select, takeLatest } from "redux-saga/effects"; -import { getWidgetImmediateChildren, getWidgets } from "./selectors"; +import { + DroppableWidgets, + WidgetTypes, + MAIN_CONTAINER_WIDGET_ID, +} from "constants/WidgetConstants"; +import { all, call, put, select, takeLatest } from "redux-saga/effects"; +import { + getWidgetImmediateChildren, + getWidgetMetaProps, + getWidgets, +} from "./selectors"; import log from "loglevel"; import { deselectMultipleWidgetsAction, @@ -14,7 +22,11 @@ import { Toaster } from "components/ads/Toast"; import { createMessage, SELECT_ALL_WIDGETS_MSG } from "constants/messages"; import { Variant } from "components/ads/common"; import { getSelectedWidget, getSelectedWidgets } from "selectors/ui"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { + CanvasWidgetsReduxState, + FlattenedWidgetProps, +} from "reducers/entityReducers/canvasWidgetsReducer"; +import { getWidgetChildren } from "./WidgetOperationUtils"; // The following is computed to be used in the entity explorer // Every time a widget is selected, we need to expand widget entities @@ -23,7 +35,7 @@ function* selectedWidgetAncestrySaga( action: ReduxAction<{ widgetId: string; isMultiSelect: boolean }>, ) { try { - const canvasWidgets = yield select(getWidgets); + const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const widgetIdsExpandList = []; const { isMultiSelect, widgetId: selectedWidget } = action.payload; @@ -34,7 +46,7 @@ function* selectedWidgetAncestrySaga( // If there is a parentId for the selectedWidget if (widgetId) { // Keep including the parent until we reach the main container - while (widgetId !== MAIN_CONTAINER_WIDGET_ID) { + while (widgetId && widgetId !== MAIN_CONTAINER_WIDGET_ID) { widgetIdsExpandList.push(widgetId); if (canvasWidgets[widgetId] && canvasWidgets[widgetId].parentId) widgetId = canvasWidgets[widgetId].parentId; @@ -60,21 +72,110 @@ function* selectedWidgetAncestrySaga( } } -function* selectAllWidgetsInCanvasSaga( - action: ReduxAction<{ canvasId: string }>, -) { - const { canvasId } = action.payload; - const allWidgetsOnCanvas: string[] = yield select( - getWidgetImmediateChildren, - canvasId, - ); - if (allWidgetsOnCanvas && allWidgetsOnCanvas.length) { - yield put(selectMultipleWidgetsAction(allWidgetsOnCanvas)); - Toaster.show({ - text: createMessage(SELECT_ALL_WIDGETS_MSG), - variant: Variant.info, - duration: 3000, +function* getDroppingCanvasOfWidget(widgetLastSelected: FlattenedWidgetProps) { + if (DroppableWidgets.includes(widgetLastSelected.type)) { + const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const childWidgets: string[] = yield select( + getWidgetImmediateChildren, + widgetLastSelected.widgetId, + ); + const firstCanvas = childWidgets.find((each) => { + const widget = canvasWidgets[each]; + return widget.type === WidgetTypes.CANVAS_WIDGET; }); + if (widgetLastSelected.type === WidgetTypes.TABS_WIDGET) { + const tabMetaProps: Record = yield select( + getWidgetMetaProps, + widgetLastSelected.widgetId, + ); + return tabMetaProps.selectedTabWidgetId; + } + if (firstCanvas) { + return firstCanvas; + } + } + return widgetLastSelected.parentId; +} + +function* getLastSelectedCanvas() { + const lastSelectedWidget: string = yield select(getSelectedWidget); + const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const widgetLastSelected = canvasWidgets[lastSelectedWidget]; + if (widgetLastSelected) { + const canvasToSelect: string = yield call( + getDroppingCanvasOfWidget, + widgetLastSelected, + ); + return canvasToSelect ? canvasToSelect : MAIN_CONTAINER_WIDGET_ID; + } + return MAIN_CONTAINER_WIDGET_ID; +} + +// used for List widget cases +const isChildOfDropDisabledCanvas = ( + canvasWidgets: CanvasWidgetsReduxState, + widgetId: string, +) => { + const widget = canvasWidgets[widgetId]; + const parentId = widget.parentId || MAIN_CONTAINER_WIDGET_ID; + const parent = canvasWidgets[parentId]; + return !!parent.dropDisabled; +}; + +function* getAllSelectableChildren() { + const lastSelectedWidget: string = yield select(getSelectedWidget); + const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const widgetLastSelected = canvasWidgets[lastSelectedWidget]; + const canvasId: string = yield call(getLastSelectedCanvas); + let allChildren: string[] = []; + const selectGrandChildren: boolean = lastSelectedWidget + ? widgetLastSelected.type === WidgetTypes.LIST_WIDGET + : false; + if (selectGrandChildren) { + allChildren = yield call( + getWidgetChildren, + canvasWidgets, + lastSelectedWidget, + ); + } else { + allChildren = yield select(getWidgetImmediateChildren, canvasId); + } + if (allChildren && allChildren.length) { + const selectableChildren = allChildren.filter((each) => { + const isCanvasWidget = + each && + canvasWidgets[each] && + canvasWidgets[each].type === WidgetTypes.CANVAS_WIDGET; + const isImmovableWidget = isChildOfDropDisabledCanvas( + canvasWidgets, + each, + ); + return !(isCanvasWidget || isImmovableWidget); + }); + return selectableChildren; + } + return []; +} + +function* selectAllWidgetsInCanvasSaga() { + const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const allSelectableChildren: string[] = yield call(getAllSelectableChildren); + if (allSelectableChildren && allSelectableChildren.length) { + yield put(selectMultipleWidgetsAction(allSelectableChildren)); + const isAnyModalSelected = allSelectableChildren.some((each) => { + return ( + each && + canvasWidgets[each] && + canvasWidgets[each].type === WidgetTypes.MODAL_WIDGET + ); + }); + if (isAnyModalSelected) { + Toaster.show({ + text: createMessage(SELECT_ALL_WIDGETS_MSG), + variant: Variant.info, + duration: 3000, + }); + } } } From 1ef071905617e48142bf25bb113e4202b3c0aaa5 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 13 Aug 2021 17:35:32 +0530 Subject: [PATCH 61/68] dip --- app/client/src/widgets/ModalWidget.tsx | 30 ++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/app/client/src/widgets/ModalWidget.tsx b/app/client/src/widgets/ModalWidget.tsx index cabfd9436f66..74ae1d26491b 100644 --- a/app/client/src/widgets/ModalWidget.tsx +++ b/app/client/src/widgets/ModalWidget.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from "react"; +import React, { createRef, ReactNode } from "react"; import { connect } from "react-redux"; import { ReduxActionTypes } from "constants/ReduxActionConstants"; @@ -18,6 +18,7 @@ import withMeta, { WithMeta } from "./MetaHOC"; import { AppState } from "reducers"; import { getWidget } from "sagas/selectors"; import { ClickContentToOpenPropPane } from "utils/hooks/useClickOpenPropPane"; +import styled from "styled-components"; const MODAL_SIZE: { [id: string]: { width: number; height: number } } = { MODAL_SMALL: { @@ -32,7 +33,31 @@ const MODAL_SIZE: { [id: string]: { width: number; height: number } } = { }, }; +const ResizableDiv = styled.div` + z-index: 1000; + resize: both; + border: 1px solid orange; + overflow: auto; +`; + export class ModalWidget extends BaseWidget { + private modal: React.RefObject; + constructor(props: any) { + super(props); + this.modal = createRef(); + } + + componentDidMount() { + if (this.modal.current) { + const resizeObserver = new ResizeObserver(function() { + () => { + console.log("I have resized"); + }; + }); + resizeObserver.observe(this.modal.current); + } + } + static getPropertyPaneConfig() { return [ { @@ -165,7 +190,7 @@ export class ModalWidget extends BaseWidget { scrollContents={!!this.props.shouldScrollContents} width={this.getModalWidth()} > - {content} + {content} ); } @@ -189,6 +214,7 @@ export class ModalWidget extends BaseWidget { export interface ModalWidgetProps extends WidgetProps, WithMeta { renderMode: RenderMode; + modal: HTMLDivElement; isOpen?: boolean; children?: WidgetProps[]; canOutsideClickClose?: boolean; From e88f85046a1908a61791c3bc8532e4e5879f29b3 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 17 Aug 2021 12:38:00 +0530 Subject: [PATCH 62/68] select from editor --- .../src/actions/canvasSelectionActions.ts | 15 +++ .../src/constants/ReduxActionConstants.tsx | 2 + app/client/src/pages/Editor/WidgetsEditor.tsx | 11 +- .../src/pages/common/CanvasSelectionArena.tsx | 104 ++++++++++++++---- .../uiReducers/canvasSelectionReducer.ts | 20 ++++ app/client/src/sagas/SelectionCanvasSagas.ts | 11 +- 6 files changed, 132 insertions(+), 31 deletions(-) diff --git a/app/client/src/actions/canvasSelectionActions.ts b/app/client/src/actions/canvasSelectionActions.ts index 8f59ecc967b7..96a28faa3a90 100644 --- a/app/client/src/actions/canvasSelectionActions.ts +++ b/app/client/src/actions/canvasSelectionActions.ts @@ -1,5 +1,20 @@ import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; import { SelectedArenaDimensions } from "pages/common/CanvasSelectionArena"; +import { XYCoord } from "react-dnd"; + +export const setCanvasSelectionFromEditor = ( + start: boolean, + startPoints?: XYCoord, +) => { + return { + type: start + ? ReduxActionTypes.START_CANVAS_SELECTION_FROM_EDITOR + : ReduxActionTypes.STOP_CANVAS_SELECTION_FROM_EDITOR, + payload: { + ...(start && startPoints ? { startPoints } : {}), + }, + }; +}; export const setCanvasSelectionStateAction = ( start: boolean, diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index ec6272804058..f6b0989c3bcd 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -121,6 +121,8 @@ export const ReduxActionTypes = { LOAD_WIDGET_PANE: "LOAD_WIDGET_PANE", ZOOM_IN_CANVAS: "ZOOM_IN_CANVAS", ZOOM_OUT_CANVAS: "ZOOM_OUT_CANVAS", + START_CANVAS_SELECTION_FROM_EDITOR: "START_CANVAS_SELECTION_FROM_EDITOR", + STOP_CANVAS_SELECTION_FROM_EDITOR: "STOP_CANVAS_SELECTION_FROM_EDITOR", START_CANVAS_SELECTION: "START_CANVAS_SELECTION", STOP_CANVAS_SELECTION: "STOP_CANVAS_SELECTION", UNDO_CANVAS_ACTION: "UNDO_CANVAS_ACTION", diff --git a/app/client/src/pages/Editor/WidgetsEditor.tsx b/app/client/src/pages/Editor/WidgetsEditor.tsx index 162bca7bea1f..2b56fa2bc7b1 100644 --- a/app/client/src/pages/Editor/WidgetsEditor.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor.tsx @@ -26,8 +26,7 @@ import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout"; import Debugger from "components/editorComponents/Debugger"; import { closePropertyPane, closeTableFilterPane } from "actions/widgetActions"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; -import { setCanvasSelectionStateAction } from "actions/canvasSelectionActions"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { setCanvasSelectionFromEditor } from "actions/canvasSelectionActions"; import CrudInfoModal from "./GeneratePage/components/CrudInfoModal"; const EditorWrapper = styled.div` @@ -106,7 +105,7 @@ function WidgetsEditor() { deselectAll && deselectAll(); dispatch(closePropertyPane()); dispatch(closeTableFilterPane()); - dispatch(setCanvasSelectionStateAction(false, MAIN_CONTAINER_WIDGET_ID)); + dispatch(setCanvasSelectionFromEditor(false)); }, [focusWidget, deselectAll]); const pageLoading = ( @@ -125,7 +124,11 @@ function WidgetsEditor() { const onDragStart = (e: any) => { e.preventDefault(); e.stopPropagation(); - dispatch(setCanvasSelectionStateAction(true, MAIN_CONTAINER_WIDGET_ID)); + const startPoints = { + x: e.clientX, + y: e.clientY, + }; + dispatch(setCanvasSelectionFromEditor(true, startPoints)); }; log.debug("Canvas rendered"); diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 7aef67a8b09b..d1c264b0504b 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -17,6 +17,7 @@ import styled from "styled-components"; import { getNearestParentCanvas } from "utils/generators"; import { useCanvasDragToScroll } from "utils/hooks/useCanvasDragToScroll"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { XYCoord } from "react-dnd"; const StyledSelectionCanvas = styled.canvas` position: absolute; @@ -109,10 +110,27 @@ export function CanvasSelectionArena({ const isCurrentWidgetDrawing = useSelector((state: AppState) => { return state.ui.canvasSelection.widgetId === widgetId; }); - const drawOnEnter = useRef(false); + const outOfCanvasStartPositions = useSelector((state: AppState) => { + return state.ui.canvasSelection.outOfCanvasStartPositions; + }); + const defaultDrawOnObj = { + canDraw: false, + startPoints: undefined, + }; + const drawOnEnterObj = useRef<{ + canDraw: boolean; + startPoints?: XYCoord; + }>(defaultDrawOnObj); useEffect(() => { - drawOnEnter.current = isDraggingForSelection && isCurrentWidgetDrawing; - }, [isDraggingForSelection, isCurrentWidgetDrawing]); + drawOnEnterObj.current = { + canDraw: isDraggingForSelection && isCurrentWidgetDrawing, + startPoints: outOfCanvasStartPositions, + }; + }, [ + isDraggingForSelection, + isCurrentWidgetDrawing, + outOfCanvasStartPositions, + ]); useCanvasDragToScroll( canvasRef, isCurrentWidgetDrawing, @@ -225,9 +243,12 @@ export function CanvasSelectionArena({ }; const onMouseEnter = (e: any) => { - if (canvasRef.current && !isDragging && drawOnEnter.current) { - firstDraw(e); - drawOnEnter.current = false; + if ( + canvasRef.current && + !isDragging && + drawOnEnterObj?.current.canDraw + ) { + firstRender(e, true); } document.body.removeEventListener("mouseup", onMouseUp); document.body.removeEventListener("click", onClick); @@ -247,28 +268,58 @@ export function CanvasSelectionArena({ } }; - const determineDirection = (pos: any) => { - if (canvasRef.current) { - const bounds = canvasRef.current.getBoundingClientRect(); - const w = bounds.width, - h = bounds.height, - x = (pos.x - bounds.left - w / 2) * (w > h ? h / w : 1), - y = (pos.y - bounds.top - h / 2) * (h > w ? w / h : 1); - - return ( - Math.round((Math.atan2(y, x) * (180 / Math.PI) + 180) / 90 + 3) % 4 - ); + const startPositionsForOutCanvasSelection = () => { + const startPoints = drawOnEnterObj.current.startPoints; + const startPositions = { + top: 0, + left: 0, + }; + if (canvasRef.current && startPoints) { + const { + height, + left, + top, + width, + } = canvasRef.current.getBoundingClientRect(); + const outOfMaxBounds = { + x: startPoints.x < left + width, + y: startPoints.y < top + height, + }; + const outOfMinBounds = { + x: startPoints.x > left, + y: startPoints.y > top, + }; + const xInRange = outOfMaxBounds.x && outOfMinBounds.x; + const yInRange = outOfMaxBounds.y && outOfMinBounds.y; + const bufferFromBoundary = 2; + startPositions.left = xInRange + ? startPoints.x - left + : outOfMinBounds.x + ? width - bufferFromBoundary + : bufferFromBoundary; + startPositions.top = yInRange + ? startPoints.y - top + : outOfMinBounds.y + ? height - bufferFromBoundary + : bufferFromBoundary; } + return startPositions; }; - const firstDraw = (e: any) => { + const firstRender = (e: any, fromOuterCanvas = false) => { if (canvasRef.current) { - console.log(determineDirection({ x: e.clientX, y: e.clientY })); isMultiSelect = e.ctrlKey || e.metaKey || e.shiftKey; - selectionRectangle.left = e.offsetX - canvasRef.current.offsetLeft; - selectionRectangle.top = e.offsetY - canvasRef.current.offsetTop; + if (fromOuterCanvas) { + const { left, top } = startPositionsForOutCanvasSelection(); + selectionRectangle.left = left; + selectionRectangle.top = top; + } else { + selectionRectangle.left = e.offsetX - canvasRef.current.offsetLeft; + selectionRectangle.top = e.offsetY - canvasRef.current.offsetTop; + } selectionRectangle.width = 0; selectionRectangle.height = 0; + isDragging = true; // bring the canvas to the top layer canvasRef.current.style.zIndex = "2"; @@ -281,7 +332,7 @@ export function CanvasSelectionArena({ (!isDraggableParent || e.ctrlKey || e.metaKey) ) { dispatch(setCanvasSelectionStateAction(true, widgetId)); - firstDraw(e); + firstRender(e); } }; const onMouseUp = () => { @@ -298,7 +349,11 @@ export function CanvasSelectionArena({ } }; const onMouseMove = (e: any) => { - if (isDragging && canvasRef.current) { + if ( + isDragging && + canvasRef.current && + !drawOnEnterObj.current.canDraw + ) { selectionRectangle.width = e.offsetX - canvasRef.current.offsetLeft - selectionRectangle.left; selectionRectangle.height = @@ -316,6 +371,9 @@ export function CanvasSelectionArena({ scrollObj.lastScrollTop = scrollParent?.scrollTop; scrollObj.lastScrollHeight = scrollParent?.scrollHeight; } + if (drawOnEnterObj.current.canDraw) { + drawOnEnterObj.current = defaultDrawOnObj; + } }; const onScroll = () => { const { diff --git a/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts b/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts index d9d29fb2952b..6dd393c3b8e3 100644 --- a/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts +++ b/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts @@ -1,9 +1,12 @@ import { createImmerReducer } from "utils/AppsmithUtils"; import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { XYCoord } from "react-dnd"; const initialState: CanvasSelectionState = { isDraggingForSelection: false, widgetId: "", + outOfCanvasStartPositions: undefined, }; export const canvasSelectionReducer = createImmerReducer(initialState, { @@ -17,12 +20,29 @@ export const canvasSelectionReducer = createImmerReducer(initialState, { [ReduxActionTypes.STOP_CANVAS_SELECTION]: (state: CanvasSelectionState) => { state.isDraggingForSelection = false; state.widgetId = ""; + state.outOfCanvasStartPositions = undefined; + }, + [ReduxActionTypes.START_CANVAS_SELECTION_FROM_EDITOR]: ( + state: CanvasSelectionState, + action: ReduxAction<{ startPoints?: XYCoord }>, + ) => { + state.isDraggingForSelection = true; + state.widgetId = MAIN_CONTAINER_WIDGET_ID; + state.outOfCanvasStartPositions = action.payload.startPoints; + }, + [ReduxActionTypes.STOP_CANVAS_SELECTION_FROM_EDITOR]: ( + state: CanvasSelectionState, + ) => { + state.isDraggingForSelection = false; + state.widgetId = ""; + state.outOfCanvasStartPositions = undefined; }, }); export type CanvasSelectionState = { isDraggingForSelection: boolean; widgetId?: string; + outOfCanvasStartPositions?: XYCoord; }; export default canvasSelectionReducer; diff --git a/app/client/src/sagas/SelectionCanvasSagas.ts b/app/client/src/sagas/SelectionCanvasSagas.ts index 0c85ce236eeb..9ce550b138e0 100644 --- a/app/client/src/sagas/SelectionCanvasSagas.ts +++ b/app/client/src/sagas/SelectionCanvasSagas.ts @@ -1,6 +1,7 @@ import { selectMultipleWidgetsAction } from "actions/widgetSelectionActions"; import { OccupiedSpace } from "constants/editorConstants"; import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { isEqual } from "lodash"; import { SelectedArenaDimensions } from "pages/common/CanvasSelectionArena"; import { all, cancel, put, select, take, takeLatest } from "redux-saga/effects"; @@ -102,10 +103,8 @@ function* startCanvasSelectionSaga( actionPayload: ReduxAction<{ widgetId: string }>, ) { const lastSelectedWidgets: string[] = yield select(getSelectedWidgets); - const mainContainer: WidgetProps = yield select( - getWidget, - actionPayload.payload.widgetId, - ); + const widgetId = actionPayload.payload.widgetId || MAIN_CONTAINER_WIDGET_ID; + const mainContainer: WidgetProps = yield select(getWidget, widgetId); const lastSelectedWidgetsWithoutParent = lastSelectedWidgets.filter( (each) => each !== mainContainer.parentId, ); @@ -133,5 +132,9 @@ export default function* selectionCanvasSagas() { ReduxActionTypes.START_CANVAS_SELECTION, startCanvasSelectionSaga, ), + takeLatest( + ReduxActionTypes.START_CANVAS_SELECTION_FROM_EDITOR, + startCanvasSelectionSaga, + ), ]); } From 27605289ba0621f14e099eacabdc73d1ebfd5a4d Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 17 Aug 2021 16:20:06 +0530 Subject: [PATCH 63/68] fixing a few bugs --- .../src/pages/common/CanvasSelectionArena.tsx | 100 ++++++++++-------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index d1c264b0504b..4518c87c3284 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -102,7 +102,7 @@ export function CanvasSelectionArena({ trailing: true, }, ), - [widgetId], + [widgetId, snapColumnSpace, snapRowSpace], ); const isDraggingForSelection = useSelector((state: AppState) => { return state.ui.canvasSelection.isDraggingForSelection; @@ -122,9 +122,14 @@ export function CanvasSelectionArena({ startPoints?: XYCoord; }>(defaultDrawOnObj); useEffect(() => { + const canDrawOnEnter = + isDraggingForSelection && + isCurrentWidgetDrawing && + !!outOfCanvasStartPositions; + drawOnEnterObj.current = { - canDraw: isDraggingForSelection && isCurrentWidgetDrawing, - startPoints: outOfCanvasStartPositions, + canDraw: canDrawOnEnter, + startPoints: canDrawOnEnter ? outOfCanvasStartPositions : undefined, }; }, [ isDraggingForSelection, @@ -150,7 +155,6 @@ export function CanvasSelectionArena({ canvasRef.current, ); const scrollObj: any = {}; - let canvasCtx: any = canvasRef.current.getContext("2d"); const initRectangle = (): SelectedArenaDimensions => ({ top: 0, @@ -162,26 +166,6 @@ export function CanvasSelectionArena({ let isMultiSelect = false; let isDragging = false; - const init = () => { - if (canvasRef.current) { - const { height, width } = canvasRef.current.getBoundingClientRect(); - if (height && width) { - canvasRef.current.width = width * scale; - canvasRef.current.height = - (snapRows * snapRowSpace + (widgetId === "0" ? 200 : 0)) * scale; - } - canvasCtx = canvasRef.current.getContext("2d"); - canvasCtx.scale(scale, scale); - canvasRef.current.addEventListener("click", onClick, false); - canvasRef.current.addEventListener("mousedown", onMouseDown, false); - canvasRef.current.addEventListener("mouseup", onMouseUp, false); - canvasRef.current.addEventListener("mousemove", onMouseMove, false); - canvasRef.current.addEventListener("mouseleave", onMouseLeave, false); - canvasRef.current.addEventListener("mouseenter", onMouseEnter, false); - scrollParent?.addEventListener("scroll", onScroll, false); - } - }; - const getSelectionDimensions = () => { return { top: @@ -238,8 +222,10 @@ export function CanvasSelectionArena({ }; const onMouseLeave = () => { - document.body.addEventListener("mouseup", onMouseUp, false); - document.body.addEventListener("click", onClick, false); + if (widgetId === MAIN_CONTAINER_WIDGET_ID) { + document.body.addEventListener("mouseup", onMouseUp, false); + document.body.addEventListener("click", onClick, false); + } }; const onMouseEnter = (e: any) => { @@ -249,9 +235,11 @@ export function CanvasSelectionArena({ drawOnEnterObj?.current.canDraw ) { firstRender(e, true); + drawOnEnterObj.current = defaultDrawOnObj; + } else if (widgetId === MAIN_CONTAINER_WIDGET_ID) { + document.body.removeEventListener("mouseup", onMouseUp); + document.body.removeEventListener("click", onClick); } - document.body.removeEventListener("mouseup", onMouseUp); - document.body.removeEventListener("click", onClick); }; const onClick = (e: any) => { @@ -307,7 +295,7 @@ export function CanvasSelectionArena({ }; const firstRender = (e: any, fromOuterCanvas = false) => { - if (canvasRef.current) { + if (canvasRef.current && !isDragging) { isMultiSelect = e.ctrlKey || e.metaKey || e.shiftKey; if (fromOuterCanvas) { const { left, top } = startPositionsForOutCanvasSelection(); @@ -349,11 +337,7 @@ export function CanvasSelectionArena({ } }; const onMouseMove = (e: any) => { - if ( - isDragging && - canvasRef.current && - !drawOnEnterObj.current.canDraw - ) { + if (isDragging && canvasRef.current) { selectionRectangle.width = e.offsetX - canvasRef.current.offsetLeft - selectionRectangle.left; selectionRectangle.height = @@ -371,9 +355,6 @@ export function CanvasSelectionArena({ scrollObj.lastScrollTop = scrollParent?.scrollTop; scrollObj.lastScrollHeight = scrollParent?.scrollHeight; } - if (drawOnEnterObj.current.canDraw) { - drawOnEnterObj.current = defaultDrawOnObj; - } }; const onScroll = () => { const { @@ -397,10 +378,17 @@ export function CanvasSelectionArena({ }); } }; - if (appMode === APP_MODE.EDIT) { - init(); - } - return () => { + + const addEventListeners = () => { + canvasRef.current?.addEventListener("click", onClick, false); + canvasRef.current?.addEventListener("mousedown", onMouseDown, false); + canvasRef.current?.addEventListener("mouseup", onMouseUp, false); + canvasRef.current?.addEventListener("mousemove", onMouseMove, false); + canvasRef.current?.addEventListener("mouseleave", onMouseLeave, false); + canvasRef.current?.addEventListener("mouseenter", onMouseEnter, false); + scrollParent?.addEventListener("scroll", onScroll, false); + }; + const removeEventListeners = () => { canvasRef.current?.removeEventListener("mousedown", onMouseDown); canvasRef.current?.removeEventListener("mouseup", onMouseUp); canvasRef.current?.removeEventListener("mousemove", onMouseMove); @@ -408,8 +396,36 @@ export function CanvasSelectionArena({ canvasRef.current?.removeEventListener("mouseenter", onMouseEnter); canvasRef.current?.removeEventListener("click", onClick); }; + const init = () => { + if (canvasRef.current) { + const { height, width } = canvasRef.current.getBoundingClientRect(); + if (height && width) { + canvasRef.current.width = width * scale; + canvasRef.current.height = + (snapRows * snapRowSpace + (widgetId === "0" ? 200 : 0)) * scale; + } + canvasCtx = canvasRef.current.getContext("2d"); + canvasCtx.scale(scale, scale); + removeEventListeners(); + addEventListeners(); + } + }; + if (appMode === APP_MODE.EDIT) { + init(); + } + return () => { + removeEventListeners(); + }; } - }, [appLayout, currentPageId, mainContainer, isDragging, snapRows]); + }, [ + appLayout, + currentPageId, + mainContainer, + isDragging, + snapRows, + snapColumnSpace, + snapRowSpace, + ]); return appMode === APP_MODE.EDIT && !(isDragging || isResizing) ? ( Date: Tue, 17 Aug 2021 16:24:15 +0530 Subject: [PATCH 64/68] Revert "dip" This reverts commit 1ef071905617e48142bf25bb113e4202b3c0aaa5. --- app/client/src/widgets/ModalWidget.tsx | 30 ++------------------------ 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/app/client/src/widgets/ModalWidget.tsx b/app/client/src/widgets/ModalWidget.tsx index 74ae1d26491b..cabfd9436f66 100644 --- a/app/client/src/widgets/ModalWidget.tsx +++ b/app/client/src/widgets/ModalWidget.tsx @@ -1,4 +1,4 @@ -import React, { createRef, ReactNode } from "react"; +import React, { ReactNode } from "react"; import { connect } from "react-redux"; import { ReduxActionTypes } from "constants/ReduxActionConstants"; @@ -18,7 +18,6 @@ import withMeta, { WithMeta } from "./MetaHOC"; import { AppState } from "reducers"; import { getWidget } from "sagas/selectors"; import { ClickContentToOpenPropPane } from "utils/hooks/useClickOpenPropPane"; -import styled from "styled-components"; const MODAL_SIZE: { [id: string]: { width: number; height: number } } = { MODAL_SMALL: { @@ -33,31 +32,7 @@ const MODAL_SIZE: { [id: string]: { width: number; height: number } } = { }, }; -const ResizableDiv = styled.div` - z-index: 1000; - resize: both; - border: 1px solid orange; - overflow: auto; -`; - export class ModalWidget extends BaseWidget { - private modal: React.RefObject; - constructor(props: any) { - super(props); - this.modal = createRef(); - } - - componentDidMount() { - if (this.modal.current) { - const resizeObserver = new ResizeObserver(function() { - () => { - console.log("I have resized"); - }; - }); - resizeObserver.observe(this.modal.current); - } - } - static getPropertyPaneConfig() { return [ { @@ -190,7 +165,7 @@ export class ModalWidget extends BaseWidget { scrollContents={!!this.props.shouldScrollContents} width={this.getModalWidth()} > - {content} + {content} ); } @@ -214,7 +189,6 @@ export class ModalWidget extends BaseWidget { export interface ModalWidgetProps extends WidgetProps, WithMeta { renderMode: RenderMode; - modal: HTMLDivElement; isOpen?: boolean; children?: WidgetProps[]; canOutsideClickClose?: boolean; From 634e38b4e3338a6f05e422deaed32ac3c8dfe015 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 17 Aug 2021 16:41:32 +0530 Subject: [PATCH 65/68] fixing bugs --- .../src/pages/common/CanvasSelectionArena.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 4518c87c3284..a918050a0706 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -18,6 +18,7 @@ import { getNearestParentCanvas } from "utils/generators"; import { useCanvasDragToScroll } from "utils/hooks/useCanvasDragToScroll"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { XYCoord } from "react-dnd"; +import { theme } from "constants/DefaultTheme"; const StyledSelectionCanvas = styled.canvas` position: absolute; @@ -121,6 +122,8 @@ export function CanvasSelectionArena({ canDraw: boolean; startPoints?: XYCoord; }>(defaultDrawOnObj); + + // start main container selection from widget editor useEffect(() => { const canDrawOnEnter = isDraggingForSelection && @@ -131,11 +134,19 @@ export function CanvasSelectionArena({ canDraw: canDrawOnEnter, startPoints: canDrawOnEnter ? outOfCanvasStartPositions : undefined, }; + if (canvasRef.current) { + if (canDrawOnEnter) { + canvasRef.current.style.zIndex = "2"; + } else { + canvasRef.current.style.zIndex = ""; + } + } }, [ isDraggingForSelection, isCurrentWidgetDrawing, outOfCanvasStartPositions, ]); + useCanvasDragToScroll( canvasRef, isCurrentWidgetDrawing, @@ -402,7 +413,11 @@ export function CanvasSelectionArena({ if (height && width) { canvasRef.current.width = width * scale; canvasRef.current.height = - (snapRows * snapRowSpace + (widgetId === "0" ? 200 : 0)) * scale; + (snapRows * snapRowSpace + + (widgetId === MAIN_CONTAINER_WIDGET_ID + ? theme.canvasBottomPadding + : 0)) * + scale; } canvasCtx = canvasRef.current.getContext("2d"); canvasCtx.scale(scale, scale); From f70b9220fc82e76caaf650486d96bd7b0215e6c0 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 17 Aug 2021 17:08:43 +0530 Subject: [PATCH 66/68] fixing specs --- app/client/src/pages/Editor/GlobalHotKeys.test.tsx | 6 ++++-- app/client/src/pages/common/CanvasSelectionArena.tsx | 8 ++------ app/client/src/sagas/WidgetSelectionSagas.ts | 3 ++- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/client/src/pages/Editor/GlobalHotKeys.test.tsx b/app/client/src/pages/Editor/GlobalHotKeys.test.tsx index fa0e188805cd..052f5140246c 100644 --- a/app/client/src/pages/Editor/GlobalHotKeys.test.tsx +++ b/app/client/src/pages/Editor/GlobalHotKeys.test.tsx @@ -52,8 +52,8 @@ describe("Select all hotkey", () => { it("Cmd + A - select all widgets on canvas", async () => { const children: any = buildChildren([ - { type: "TABS_WIDGET" }, - { type: "SWITCH_WIDGET" }, + { type: "TABS_WIDGET", parentId: MAIN_CONTAINER_WIDGET_ID }, + { type: "SWITCH_WIDGET", parentId: MAIN_CONTAINER_WIDGET_ID }, ]); const dsl: any = widgetCanvasFactory.build({ children, @@ -156,6 +156,7 @@ describe("Cut/Copy/Paste hotkey", () => { bottomRow: 30, leftColumn: 5, rightColumn: 30, + parentId: MAIN_CONTAINER_WIDGET_ID, }, { type: "SWITCH_WIDGET", @@ -163,6 +164,7 @@ describe("Cut/Copy/Paste hotkey", () => { bottomRow: 10, leftColumn: 40, rightColumn: 48, + parentId: MAIN_CONTAINER_WIDGET_ID, }, ]); const dsl: any = widgetCanvasFactory.build({ diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index a918050a0706..1aa1fba5fd4b 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -134,12 +134,8 @@ export function CanvasSelectionArena({ canDraw: canDrawOnEnter, startPoints: canDrawOnEnter ? outOfCanvasStartPositions : undefined, }; - if (canvasRef.current) { - if (canDrawOnEnter) { - canvasRef.current.style.zIndex = "2"; - } else { - canvasRef.current.style.zIndex = ""; - } + if (canvasRef.current && canDrawOnEnter) { + canvasRef.current.style.zIndex = "2"; } }, [ isDraggingForSelection, diff --git a/app/client/src/sagas/WidgetSelectionSagas.ts b/app/client/src/sagas/WidgetSelectionSagas.ts index 792fd2819351..dcb9eddcfc80 100644 --- a/app/client/src/sagas/WidgetSelectionSagas.ts +++ b/app/client/src/sagas/WidgetSelectionSagas.ts @@ -101,7 +101,8 @@ function* getDroppingCanvasOfWidget(widgetLastSelected: FlattenedWidgetProps) { function* getLastSelectedCanvas() { const lastSelectedWidget: string = yield select(getSelectedWidget); const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const widgetLastSelected = canvasWidgets[lastSelectedWidget]; + const widgetLastSelected = + lastSelectedWidget && canvasWidgets[lastSelectedWidget]; if (widgetLastSelected) { const canvasToSelect: string = yield call( getDroppingCanvasOfWidget, From c28bc51039b8c22f923a9228488d8a2b0e3774f1 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 18 Aug 2021 19:32:16 +0530 Subject: [PATCH 67/68] Adding new specs. --- .../src/constants/ReduxActionConstants.tsx | 1 + .../src/pages/Editor/GlobalHotKeys.test.tsx | 328 ++++++++++++++---- .../src/pages/Editor/MainContainer.test.tsx | 7 +- app/client/src/pages/Editor/WidgetsEditor.tsx | 1 + .../common/CanvasSelectionArena.test.tsx | 257 +++++++++++++- app/client/src/sagas/WidgetSelectionSagas.ts | 196 +++++++---- .../test/factories/Widgets/CanvasFactory.ts | 1 + .../test/factories/Widgets/ListFactory.ts | 3 +- app/client/test/testMockedWidgets.tsx | 7 +- 9 files changed, 654 insertions(+), 147 deletions(-) diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index f6b0989c3bcd..b21cb865dbb8 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -538,6 +538,7 @@ export const ReduxActionErrorTypes = { SAVE_PAGE_ERROR: "SAVE_PAGE_ERROR", FETCH_WIDGET_CARDS_ERROR: "FETCH_WIDGET_CARDS_ERROR", WIDGET_OPERATION_ERROR: "WIDGET_OPERATION_ERROR", + WIDGET_SELECTION_ERROR: "WIDGET_SELECTION_ERROR", FETCH_PROPERTY_PANE_CONFIGS_ERROR: "FETCH_PROPERTY_PANE_CONFIGS_ERROR", FETCH_CONFIGS_ERROR: "FETCH_CONFIGS_ERROR", PROPERTY_PANE_ERROR: "PROPERTY_PANE_ERROR", diff --git a/app/client/src/pages/Editor/GlobalHotKeys.test.tsx b/app/client/src/pages/Editor/GlobalHotKeys.test.tsx index 052f5140246c..5aa199729731 100644 --- a/app/client/src/pages/Editor/GlobalHotKeys.test.tsx +++ b/app/client/src/pages/Editor/GlobalHotKeys.test.tsx @@ -20,7 +20,8 @@ import { } from "test/testCommon"; import { MockCanvas } from "test/testMockedWidgets"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -describe("Select all hotkey", () => { +import { generateReactKey } from "utils/generators"; +describe("Canvas Hot Keys", () => { const mockGetIsFetchingPage = jest.spyOn(utilities, "getIsFetchingPage"); const spyGetCanvasWidgetDsl = jest.spyOn(utilities, "getCanvasWidgetDsl"); @@ -50,65 +51,45 @@ describe("Select all hotkey", () => { })); }); - it("Cmd + A - select all widgets on canvas", async () => { - const children: any = buildChildren([ - { type: "TABS_WIDGET", parentId: MAIN_CONTAINER_WIDGET_ID }, - { type: "SWITCH_WIDGET", parentId: MAIN_CONTAINER_WIDGET_ID }, - ]); - const dsl: any = widgetCanvasFactory.build({ - children, - }); - spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); - mockGetIsFetchingPage.mockImplementation(() => false); + describe("Select all hotkey", () => { + it("Cmd + A - select all widgets on canvas", async () => { + const children: any = buildChildren([ + { type: "TABS_WIDGET", parentId: MAIN_CONTAINER_WIDGET_ID }, + { type: "SWITCH_WIDGET", parentId: MAIN_CONTAINER_WIDGET_ID }, + ]); + const dsl: any = widgetCanvasFactory.build({ + children, + }); + spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); + mockGetIsFetchingPage.mockImplementation(() => false); - const component = render( - - - - - - - , - { initialState: store.getState(), sagasToRun: sagasToRunForTests }, - ); - let propPane = component.queryByTestId("t--propertypane"); - expect(propPane).toBeNull(); - const canvasWidgets = component.queryAllByTestId("test-widget"); - expect(canvasWidgets.length).toBe(2); - if (canvasWidgets[1].firstChild) { - fireEvent.mouseOver(canvasWidgets[1].firstChild); - fireEvent.click(canvasWidgets[1].firstChild); - } - propPane = component.queryByTestId("t--propertypane"); - expect(propPane).not.toBeNull(); + const component = render( + + + + + + + , + { initialState: store.getState(), sagasToRun: sagasToRunForTests }, + ); + let propPane = component.queryByTestId("t--propertypane"); + expect(propPane).toBeNull(); + const canvasWidgets = component.queryAllByTestId("test-widget"); + expect(canvasWidgets.length).toBe(2); + if (canvasWidgets[1].firstChild) { + fireEvent.mouseOver(canvasWidgets[1].firstChild); + fireEvent.click(canvasWidgets[1].firstChild); + } + propPane = component.queryByTestId("t--propertypane"); + expect(propPane).not.toBeNull(); - const artBoard: any = component.queryByTestId("t--canvas-artboard"); - // deselect all other widgets - fireEvent.click(artBoard); + const artBoard: any = component.queryByTestId("t--canvas-artboard"); + // deselect all other widgets + fireEvent.click(artBoard); - dispatchTestKeyboardEventWithCode( - component.container, - "keydown", - "A", - 65, - false, - true, - ); - let selectedWidgets = component.queryAllByTestId("t--selected"); - expect(selectedWidgets.length).toBe(2); - dispatchTestKeyboardEventWithCode( - component.container, - "keydown", - "escape", - 27, - false, - false, - ); - selectedWidgets = component.queryAllByTestId("t--selected"); - expect(selectedWidgets.length).toBe(0); - act(() => { dispatchTestKeyboardEventWithCode( component.container, "keydown", @@ -117,33 +98,246 @@ describe("Select all hotkey", () => { false, true, ); + let selectedWidgets = component.queryAllByTestId("t--selected"); + expect(selectedWidgets.length).toBe(2); + dispatchTestKeyboardEventWithCode( + component.container, + "keydown", + "escape", + 27, + false, + false, + ); + selectedWidgets = component.queryAllByTestId("t--selected"); + expect(selectedWidgets.length).toBe(0); + act(() => { + dispatchTestKeyboardEventWithCode( + component.container, + "keydown", + "A", + 65, + false, + true, + ); + }); + + selectedWidgets = component.queryAllByTestId("t--selected"); + expect(selectedWidgets.length).toBe(2); + act(() => { + dispatchTestKeyboardEventWithCode( + component.container, + "keydown", + "C", + 67, + false, + true, + ); + }); + act(() => { + dispatchTestKeyboardEventWithCode( + component.container, + "keydown", + "V", + 86, + false, + true, + ); + }); + selectedWidgets = component.queryAllByTestId("t--selected"); + expect(selectedWidgets.length).toBe(2); }); + it("Cmd + A - select all widgets inside last selected container", async () => { + const containerId = generateReactKey(); + const canvasId = generateReactKey(); + const children: any = buildChildren([ + { type: "CHECKBOX_WIDGET", parentId: canvasId }, + { type: "SWITCH_WIDGET", parentId: canvasId }, + { type: "BUTTON_WIDGET", parentId: canvasId }, + ]); + const canvasWidget = buildChildren([ + { + type: "CANVAS_WIDGET", + parentId: containerId, + children, + widgetId: canvasId, + }, + ]); + const containerChildren: any = buildChildren([ + { + type: "CONTAINER_WIDGET", + children: canvasWidget, + widgetId: containerId, + parentId: "0", + }, + { type: "CHART_WIDGET", parentId: "0" }, + ]); + const dsl: any = widgetCanvasFactory.build({ + children: containerChildren, + }); + spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); + mockGetIsFetchingPage.mockImplementation(() => false); + + const component = render( + + + + + + + , + { initialState: store.getState(), sagasToRun: sagasToRunForTests }, + ); + const propPane = component.queryByTestId("t--propertypane"); + expect(propPane).toBeNull(); + const canvasWidgets = component.queryAllByTestId("test-widget"); + expect(canvasWidgets.length).toBe(5); + if (canvasWidgets[0].firstChild) { + fireEvent.mouseOver(canvasWidgets[0].firstChild); + fireEvent.click(canvasWidgets[0].firstChild); + } - selectedWidgets = component.queryAllByTestId("t--selected"); - expect(selectedWidgets.length).toBe(2); - act(() => { dispatchTestKeyboardEventWithCode( component.container, "keydown", - "C", - 67, + "A", + 65, false, true, ); + const selectedWidgets = component.queryAllByTestId("t--selected"); + expect(selectedWidgets.length).toBe(children.length); }); - act(() => { + it("Cmd + A - select all widgets inside a form", async () => { + const children: any = buildChildren([ + { type: "FORM_WIDGET", parentId: MAIN_CONTAINER_WIDGET_ID }, + ]); + const dsl: any = widgetCanvasFactory.build({ + children, + }); + spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); + mockGetIsFetchingPage.mockImplementation(() => false); + + const component = render( + + + + + + + , + { initialState: store.getState(), sagasToRun: sagasToRunForTests }, + ); + const propPane = component.queryByTestId("t--propertypane"); + expect(propPane).toBeNull(); + const canvasWidgets = component.queryAllByTestId("test-widget"); + expect(canvasWidgets.length).toBe(4); + if (canvasWidgets[0].firstChild) { + fireEvent.mouseOver(canvasWidgets[0].firstChild); + fireEvent.click(canvasWidgets[0].firstChild); + } + dispatchTestKeyboardEventWithCode( component.container, "keydown", - "V", - 86, + "A", + 65, false, true, ); + const selectedWidgets = component.queryAllByTestId("t--selected"); + expect(selectedWidgets.length).toBe(3); + }); + it("Cmd + A - select all widgets inside a list", async () => { + const listId = generateReactKey(); + const containerId = generateReactKey(); + const canvasId = generateReactKey(); + const listCanvasId = generateReactKey(); + const children: any = buildChildren([ + { type: "CHECKBOX_WIDGET", parentId: canvasId }, + { type: "SWITCH_WIDGET", parentId: canvasId }, + { type: "BUTTON_WIDGET", parentId: canvasId }, + ]); + const canvasWidget = buildChildren([ + { + type: "CANVAS_WIDGET", + parentId: containerId, + children, + widgetId: canvasId, + bottomRow: 20, + }, + ]); + const containerChildren: any = buildChildren([ + { + type: "CONTAINER_WIDGET", + children: canvasWidget, + widgetId: containerId, + parentId: listCanvasId, + dropDisabled: true, + bottomRow: 4, + }, + ]); + const listCanvasChildren: any = buildChildren([ + { + type: "CANVAS_WIDGET", + children: containerChildren, + widgetId: listCanvasId, + dropDisabled: true, + parentId: listId, + bottomRow: 20, + }, + ]); + const listChildren: any = buildChildren([ + { + type: "LIST_WIDGET", + children: listCanvasChildren, + widgetId: listId, + parentId: "0", + }, + ]); + const dsl: any = widgetCanvasFactory.build({ + children: listChildren, + }); + spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); + mockGetIsFetchingPage.mockImplementation(() => false); + + const component = render( + + + + + + + , + { initialState: store.getState(), sagasToRun: sagasToRunForTests }, + ); + const propPane = component.queryByTestId("t--propertypane"); + expect(propPane).toBeNull(); + const canvasWidgets = component.queryAllByTestId("test-widget"); + + if (canvasWidgets[0].firstChild) { + fireEvent.mouseOver(canvasWidgets[0].firstChild); + fireEvent.click(canvasWidgets[0].firstChild); + } + + dispatchTestKeyboardEventWithCode( + component.container, + "keydown", + "A", + 65, + false, + true, + ); + const selectedWidgets = component.queryAllByTestId("t--selected"); + expect(selectedWidgets.length).toBe(3); }); - selectedWidgets = component.queryAllByTestId("t--selected"); - expect(selectedWidgets.length).toBe(2); }); + afterAll(() => jest.resetModules()); }); diff --git a/app/client/src/pages/Editor/MainContainer.test.tsx b/app/client/src/pages/Editor/MainContainer.test.tsx index 256547890034..21ad67a8d522 100644 --- a/app/client/src/pages/Editor/MainContainer.test.tsx +++ b/app/client/src/pages/Editor/MainContainer.test.tsx @@ -5,7 +5,6 @@ import { } from "test/factories/WidgetFactoryUtils"; import { act, render, fireEvent } from "test/testUtils"; import GlobalHotKeys from "./GlobalHotKeys"; -import MainContainer from "./MainContainer"; import { MemoryRouter } from "react-router-dom"; import * as utilities from "selectors/editorSelectors"; import store from "store"; @@ -15,18 +14,14 @@ import { MockApplication, mockGetCanvasWidgetDsl, syntheticTestMouseEvent, - useMockDsl, } from "test/testCommon"; import lodash from "lodash"; import { getAbsolutePixels } from "utils/helpers"; +import { UpdatedMainContainer } from "test/testMockedWidgets"; describe("Drag and Drop widgets into Main container", () => { const mockGetIsFetchingPage = jest.spyOn(utilities, "getIsFetchingPage"); const spyGetCanvasWidgetDsl = jest.spyOn(utilities, "getCanvasWidgetDsl"); - function UpdatedMainContainer({ dsl }: any) { - useMockDsl(dsl); - return ; - } // These need to be at the top to avoid imports not being mocked. ideally should be in setup.ts but will override for all other tests beforeAll(() => { const mockGenerator = function*() { diff --git a/app/client/src/pages/Editor/WidgetsEditor.tsx b/app/client/src/pages/Editor/WidgetsEditor.tsx index 2b56fa2bc7b1..785cdcc3332f 100644 --- a/app/client/src/pages/Editor/WidgetsEditor.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor.tsx @@ -136,6 +136,7 @@ function WidgetsEditor() { return ( { it("Should select using canvas draw", () => { @@ -124,4 +136,245 @@ describe("Canvas selection test cases", () => { const selectedWidgets = component.queryAllByTestId("t--selected"); expect(selectedWidgets.length).toBe(2); }); + + it("Should allow draw to select using cmd + draw in Container component", () => { + const containerId = generateReactKey(); + const canvasId = generateReactKey(); + const children: any = buildChildren([ + { type: "CHECKBOX_WIDGET", parentId: canvasId }, + { type: "SWITCH_WIDGET", parentId: canvasId }, + { type: "BUTTON_WIDGET", parentId: canvasId }, + ]); + const canvasWidget = buildChildren([ + { + type: "CANVAS_WIDGET", + parentId: containerId, + children, + widgetId: canvasId, + }, + ]); + const containerChildren: any = buildChildren([ + { + type: "CONTAINER_WIDGET", + children: canvasWidget, + widgetId: containerId, + parentId: "0", + }, + { type: "CHART_WIDGET", parentId: "0" }, + ]); + const dsl: any = widgetCanvasFactory.build({ + children: containerChildren, + }); + + const component = render( + + + , + ); + let selectionCanvas: any = component.queryByTestId(`canvas-${canvasId}`); + expect(selectionCanvas.style.zIndex).toBe(""); + fireEvent.mouseDown(selectionCanvas); + // should not allow draw when cmd/ctrl is not pressed + selectionCanvas = component.queryByTestId(`canvas-${canvasId}`); + expect(selectionCanvas.style.zIndex).toBe(""); + fireEvent.mouseDown(selectionCanvas, { + metaKey: true, + }); + + selectionCanvas = component.queryByTestId(`canvas-${canvasId}`); + + expect(selectionCanvas.style.zIndex).toBe("2"); + fireEvent.mouseUp(selectionCanvas); + selectionCanvas = component.queryByTestId(`canvas-${canvasId}`); + + expect(selectionCanvas.style.zIndex).toBe(""); + }); + + it("Should select all elements inside a CONTAINER using draw on canvas from top to bottom", () => { + const containerId = generateReactKey(); + const canvasId = generateReactKey(); + const children: any = buildChildren([ + { + type: "CHECKBOX_WIDGET", + parentColumnSpace: 10, + parentRowSpace: 10, + parentId: canvasId, + }, + { + type: "BUTTON_WIDGET", + parentColumnSpace: 10, + parentRowSpace: 10, + parentId: canvasId, + }, + ]); + const canvasWidget = buildChildren([ + { + type: "CANVAS_WIDGET", + parentId: containerId, + bottomRow: 20, + rightColumn: 20, + children, + widgetId: canvasId, + }, + ]); + const containerChildren: any = buildChildren([ + { + type: "CONTAINER_WIDGET", + children: canvasWidget, + widgetId: containerId, + parentColumnSpace: 10, + parentRowSpace: 10, + bottomRow: 20, + rightColumn: 20, + parentId: "0", + }, + { type: "CHART_WIDGET", parentId: "0" }, + ]); + const dsl: any = widgetCanvasFactory.build({ + children: containerChildren, + }); + const component = render( + + + , + ); + const selectionCanvas: any = component.queryByTestId(`canvas-${canvasId}`); + fireEvent( + selectionCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousedown", { + metaKey: true, + bubbles: true, + cancelable: true, + }), + { + offsetX: 10, + offsetY: 10, + }, + ), + ); + fireEvent( + selectionCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + metaKey: true, + bubbles: true, + cancelable: true, + }), + { + offsetX: 800, + offsetY: 800, + }, + ), + ); + + fireEvent( + selectionCanvas, + syntheticTestMouseEvent( + new MouseEvent("mouseup", { + metaKey: true, + bubbles: true, + cancelable: true, + }), + { + offsetX: 800, + offsetY: 800, + }, + ), + ); + const selectedWidgets = component.queryAllByTestId("t--selected"); + expect(selectedWidgets.length).toBe(children.length); + }); + + it("Draw to select from outside of canvas(editor) ", () => { + const mockGetIsFetchingPage = jest.spyOn(utilities, "getIsFetchingPage"); + const spyGetCanvasWidgetDsl = jest.spyOn(utilities, "getCanvasWidgetDsl"); + const children: any = buildChildren([ + { + type: "TABS_WIDGET", + topRow: 1, + bottomRow: 3, + leftColumn: 1, + rightColumn: 3, + }, + { + type: "SWITCH_WIDGET", + topRow: 1, + bottomRow: 2, + leftColumn: 5, + rightColumn: 13, + }, + ]); + const dsl: any = widgetCanvasFactory.build({ + children, + }); + spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); + mockGetIsFetchingPage.mockImplementation(() => false); + + const component = render( + + + + + + + , + { initialState: store.getState(), sagasToRun: sagasToRunForTests }, + ); + const widgetEditor: any = component.queryByTestId("widgets-editor"); + let selectionCanvas: any = component.queryByTestId( + `canvas-${MAIN_CONTAINER_WIDGET_ID}`, + ); + expect(selectionCanvas.style.zIndex).toBe(""); + act(() => { + fireEvent.dragStart(widgetEditor); + }); + + selectionCanvas = component.queryByTestId( + `canvas-${MAIN_CONTAINER_WIDGET_ID}`, + ); + + expect(selectionCanvas.style.zIndex).toBe("2"); + fireEvent.mouseEnter(selectionCanvas); + fireEvent.mouseUp(selectionCanvas); + selectionCanvas = component.queryByTestId( + `canvas-${MAIN_CONTAINER_WIDGET_ID}`, + ); + + expect(selectionCanvas.style.zIndex).toBe(""); + act(() => { + fireEvent.dragStart(widgetEditor); + }); + fireEvent( + selectionCanvas, + syntheticTestMouseEvent( + new MouseEvent("mousemove", { + bubbles: true, + cancelable: true, + }), + { + offsetX: dsl.rightColumn * 4, + offsetY: dsl.bottomRow * 4, + }, + ), + ); + + fireEvent( + selectionCanvas, + syntheticTestMouseEvent( + new MouseEvent("mouseup", { + bubbles: true, + cancelable: true, + }), + { + offsetX: dsl.rightColumn * 4, + offsetY: dsl.bottomRow * 4, + }, + ), + ); + const selectedWidgets = component.queryAllByTestId("t--selected"); + expect(selectedWidgets.length).toBe(2); + }); }); diff --git a/app/client/src/sagas/WidgetSelectionSagas.ts b/app/client/src/sagas/WidgetSelectionSagas.ts index dcb9eddcfc80..89a56b570b28 100644 --- a/app/client/src/sagas/WidgetSelectionSagas.ts +++ b/app/client/src/sagas/WidgetSelectionSagas.ts @@ -1,4 +1,8 @@ -import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; +import { + ReduxAction, + ReduxActionErrorTypes, + ReduxActionTypes, +} from "constants/ReduxActionConstants"; import { DroppableWidgets, WidgetTypes, @@ -121,7 +125,7 @@ const isChildOfDropDisabledCanvas = ( const widget = canvasWidgets[widgetId]; const parentId = widget.parentId || MAIN_CONTAINER_WIDGET_ID; const parent = canvasWidgets[parentId]; - return !!parent.dropDisabled; + return !!parent?.dropDisabled; }; function* getAllSelectableChildren() { @@ -160,103 +164,155 @@ function* getAllSelectableChildren() { } function* selectAllWidgetsInCanvasSaga() { - const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const allSelectableChildren: string[] = yield call(getAllSelectableChildren); - if (allSelectableChildren && allSelectableChildren.length) { - yield put(selectMultipleWidgetsAction(allSelectableChildren)); - const isAnyModalSelected = allSelectableChildren.some((each) => { - return ( - each && - canvasWidgets[each] && - canvasWidgets[each].type === WidgetTypes.MODAL_WIDGET - ); - }); - if (isAnyModalSelected) { - Toaster.show({ - text: createMessage(SELECT_ALL_WIDGETS_MSG), - variant: Variant.info, - duration: 3000, + try { + const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const allSelectableChildren: string[] = yield call( + getAllSelectableChildren, + ); + if (allSelectableChildren && allSelectableChildren.length) { + yield put(selectMultipleWidgetsAction(allSelectableChildren)); + const isAnyModalSelected = allSelectableChildren.some((each) => { + return ( + each && + canvasWidgets[each] && + canvasWidgets[each].type === WidgetTypes.MODAL_WIDGET + ); }); + if (isAnyModalSelected) { + Toaster.show({ + text: createMessage(SELECT_ALL_WIDGETS_MSG), + variant: Variant.info, + duration: 3000, + }); + } } + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_SELECTION_ERROR, + payload: { + action: ReduxActionTypes.SELECT_ALL_WIDGETS_IN_CANVAS_INIT, + error, + }, + }); } } function* deselectNonSiblingsOfWidgetSaga( action: ReduxAction<{ widgetId: string; isMultiSelect: boolean }>, ) { - const { isMultiSelect, widgetId } = action.payload; - if (isMultiSelect) { - const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const parentId: any = allWidgets[widgetId].parentId; - const childWidgets: string[] = yield select( - getWidgetImmediateChildren, - parentId, - ); - const currentSelectedWidgets: string[] = yield select(getSelectedWidgets); + try { + const { isMultiSelect, widgetId } = action.payload; + if (isMultiSelect) { + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const parentId: any = allWidgets[widgetId].parentId; + const childWidgets: string[] = yield select( + getWidgetImmediateChildren, + parentId, + ); + const currentSelectedWidgets: string[] = yield select(getSelectedWidgets); - const nonSiblings = currentSelectedWidgets.filter( - (each) => !childWidgets.includes(each), - ); - if (nonSiblings && nonSiblings.length) { - yield put( - deselectMultipleWidgetsAction( - nonSiblings.filter((each) => each !== widgetId), - ), + const nonSiblings = currentSelectedWidgets.filter( + (each) => !childWidgets.includes(each), ); + if (nonSiblings && nonSiblings.length) { + yield put( + deselectMultipleWidgetsAction( + nonSiblings.filter((each) => each !== widgetId), + ), + ); + } } + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_SELECTION_ERROR, + payload: { + action: ReduxActionTypes.SELECT_WIDGET_INIT, + error, + }, + }); } } function* selectWidgetSaga( action: ReduxAction<{ widgetId: string; isMultiSelect: boolean }>, ) { - const { isMultiSelect, widgetId } = action.payload; - yield put(selectWidgetAction(widgetId, isMultiSelect)); + try { + const { isMultiSelect, widgetId } = action.payload; + yield put(selectWidgetAction(widgetId, isMultiSelect)); + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_SELECTION_ERROR, + payload: { + action: ReduxActionTypes.SELECT_WIDGET_INIT, + error, + }, + }); + } } function* shiftSelectWidgetsSaga( action: ReduxAction<{ widgetId: string; siblingWidgets: string[] }>, ) { - const { siblingWidgets, widgetId } = action.payload; - const selectedWidgets: string[] = yield select(getSelectedWidgets); - const lastSelectedWidget: string = yield select(getSelectedWidget); - const lastSelectedWidgetIndex = siblingWidgets.indexOf(lastSelectedWidget); - const isWidgetSelected = selectedWidgets.includes(widgetId); - if (!isWidgetSelected && lastSelectedWidgetIndex > -1) { - const selectedWidgetIndex = siblingWidgets.indexOf(widgetId); - const start = - lastSelectedWidgetIndex < selectedWidgetIndex - ? lastSelectedWidgetIndex - : selectedWidgetIndex; - const end = - lastSelectedWidgetIndex < selectedWidgetIndex - ? selectedWidgetIndex - : lastSelectedWidgetIndex; - const unSelectedSiblings = siblingWidgets.slice(start + 1, end); - if (unSelectedSiblings && unSelectedSiblings.length) { - yield put(silentAddSelectionsAction(unSelectedSiblings)); + try { + const { siblingWidgets, widgetId } = action.payload; + const selectedWidgets: string[] = yield select(getSelectedWidgets); + const lastSelectedWidget: string = yield select(getSelectedWidget); + const lastSelectedWidgetIndex = siblingWidgets.indexOf(lastSelectedWidget); + const isWidgetSelected = selectedWidgets.includes(widgetId); + if (!isWidgetSelected && lastSelectedWidgetIndex > -1) { + const selectedWidgetIndex = siblingWidgets.indexOf(widgetId); + const start = + lastSelectedWidgetIndex < selectedWidgetIndex + ? lastSelectedWidgetIndex + : selectedWidgetIndex; + const end = + lastSelectedWidgetIndex < selectedWidgetIndex + ? selectedWidgetIndex + : lastSelectedWidgetIndex; + const unSelectedSiblings = siblingWidgets.slice(start + 1, end); + if (unSelectedSiblings && unSelectedSiblings.length) { + yield put(silentAddSelectionsAction(unSelectedSiblings)); + } } + yield put(selectWidgetInitAction(widgetId, true)); + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_SELECTION_ERROR, + payload: { + action: ReduxActionTypes.SHIFT_SELECT_WIDGET_INIT, + error, + }, + }); } - yield put(selectWidgetInitAction(widgetId, true)); } function* selectMultipleWidgetsSaga( action: ReduxAction<{ widgetIds: string[] }>, ) { - const { widgetIds } = action.payload; - if (!widgetIds || !widgetIds.length) { - return; - } - const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const parentToMatch = allWidgets[widgetIds[0]].parentId; - const doesNotMatchParent = widgetIds.some((each) => { - return allWidgets[each].parentId !== parentToMatch; - }); - if (doesNotMatchParent) { - return; - } else { - yield put(selectWidgetAction()); - yield put(selectMultipleWidgetsAction(widgetIds)); + try { + const { widgetIds } = action.payload; + if (!widgetIds || !widgetIds.length) { + return; + } + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const parentToMatch = allWidgets[widgetIds[0]].parentId; + const doesNotMatchParent = widgetIds.some((each) => { + return allWidgets[each].parentId !== parentToMatch; + }); + if (doesNotMatchParent) { + return; + } else { + yield put(selectWidgetAction()); + yield put(selectMultipleWidgetsAction(widgetIds)); + } + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_SELECTION_ERROR, + payload: { + action: ReduxActionTypes.SELECT_MULTIPLE_WIDGETS_INIT, + error, + }, + }); } } diff --git a/app/client/test/factories/Widgets/CanvasFactory.ts b/app/client/test/factories/Widgets/CanvasFactory.ts index e23c26bca639..5cb499428f41 100644 --- a/app/client/test/factories/Widgets/CanvasFactory.ts +++ b/app/client/test/factories/Widgets/CanvasFactory.ts @@ -13,6 +13,7 @@ export const CanvasFactory = Factory.Sync.makeFactory({ snapRows: 33, parentRowSpace: 1, type: "CANVAS_WIDGET", + isVisible: true, canExtend: true, minHeight: 1292, parentColumnSpace: 1, diff --git a/app/client/test/factories/Widgets/ListFactory.ts b/app/client/test/factories/Widgets/ListFactory.ts index 4da12f41e4f6..960174ea3bfb 100644 --- a/app/client/test/factories/Widgets/ListFactory.ts +++ b/app/client/test/factories/Widgets/ListFactory.ts @@ -6,13 +6,14 @@ export const ListFactory = Factory.Sync.makeFactory({ image: "", defaultImage: "", type: "LIST_WIDGET", + template: {}, parentId: "Container1", parentColumnSpace: 2, parentRowSpace: 3, leftColumn: 2, rightColumn: 3, topRow: 1, - bottomRow: 3, + bottomRow: 10, isLoading: false, listData: [], disablePropertyPane: false, diff --git a/app/client/test/testMockedWidgets.tsx b/app/client/test/testMockedWidgets.tsx index eea110370e72..b5dee0b7720a 100644 --- a/app/client/test/testMockedWidgets.tsx +++ b/app/client/test/testMockedWidgets.tsx @@ -1,9 +1,14 @@ import Canvas from "pages/Editor/Canvas"; +import MainContainer from "pages/Editor/MainContainer"; import React from "react"; import { useSelector } from "react-redux"; -import { mockGetCanvasWidgetDsl } from "./testCommon"; +import { mockGetCanvasWidgetDsl, useMockDsl } from "./testCommon"; export const MockCanvas = () => { const dsl = useSelector(mockGetCanvasWidgetDsl); return ; }; +export function UpdatedMainContainer({ dsl }: any) { + useMockDsl(dsl); + return ; +} From 8d1255ab58510aed326553e06a0a8debaeb7af1f Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 24 Aug 2021 12:16:28 +0530 Subject: [PATCH 68/68] addressing code review comments. --- app/client/src/actions/canvasSelectionActions.ts | 4 ++-- .../editorComponents/ResizableComponent.tsx | 6 +++--- .../editorComponents/ResizableUtils.tsx | 6 +++--- .../src/pages/common/CanvasSelectionArena.tsx | 4 ++-- .../uiReducers/canvasSelectionReducer.ts | 6 +++--- app/client/src/utils/WidgetPropsUtils.tsx | 16 ++++++++-------- .../utils/hooks/useBlocksToBeDraggedOnCanvas.ts | 8 ++++---- app/client/src/utils/hooks/useCanvasDragging.ts | 5 +++++ 8 files changed, 30 insertions(+), 25 deletions(-) diff --git a/app/client/src/actions/canvasSelectionActions.ts b/app/client/src/actions/canvasSelectionActions.ts index 96a28faa3a90..1c14985c0e20 100644 --- a/app/client/src/actions/canvasSelectionActions.ts +++ b/app/client/src/actions/canvasSelectionActions.ts @@ -1,10 +1,10 @@ import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; import { SelectedArenaDimensions } from "pages/common/CanvasSelectionArena"; -import { XYCoord } from "react-dnd"; +import { XYCord } from "utils/hooks/useCanvasDragging"; export const setCanvasSelectionFromEditor = ( start: boolean, - startPoints?: XYCoord, + startPoints?: XYCord, ) => { return { type: start diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 3bd2e80824d5..4d9fc88a421b 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -1,5 +1,5 @@ import React, { useContext, useRef, memo, useMemo } from "react"; -import { XYCoord } from "react-dnd"; +import { XYCord } from "utils/hooks/useCanvasDragging"; import { WidgetOperations, @@ -132,7 +132,7 @@ export const ResizableComponent = memo(function ResizableComponent( // Checks if the current resize position has any collisions // If yes, set isColliding flag to true. // If no, set isColliding flag to false. - const isColliding = (newDimensions: UIElementSize, position: XYCoord) => { + const isColliding = (newDimensions: UIElementSize, position: XYCord) => { // Moving the bounding element calculations inside // to make this expensive operation only whne const boundingElementClientRect = boundingElement @@ -231,7 +231,7 @@ export const ResizableComponent = memo(function ResizableComponent( // 1) There is no collision // 2) There is a change in widget size // Update widget, if both of the above are true. - const updateSize = (newDimensions: UIElementSize, position: XYCoord) => { + const updateSize = (newDimensions: UIElementSize, position: XYCord) => { // Get the difference in size of the widget, before and after resizing. const delta: UIElementSize = { height: newDimensions.height - dimensions.height, diff --git a/app/client/src/components/editorComponents/ResizableUtils.tsx b/app/client/src/components/editorComponents/ResizableUtils.tsx index 0a0fc4cb1f6f..08f908f6a3cf 100644 --- a/app/client/src/components/editorComponents/ResizableUtils.tsx +++ b/app/client/src/components/editorComponents/ResizableUtils.tsx @@ -1,4 +1,4 @@ -import { XYCoord } from "react-dnd"; +import { XYCord } from "utils/hooks/useCanvasDragging"; import { WidgetProps, WidgetRowCols } from "widgets/BaseWidget"; import { GridDefaults } from "constants/WidgetConstants"; @@ -8,7 +8,7 @@ export const RESIZABLE_CONTAINER_BORDER_THEME_INDEX = 1; export const computeRowCols = ( delta: UIElementSize, - position: XYCoord, + position: XYCord, props: WidgetProps, ) => { return { @@ -51,7 +51,7 @@ export const hasRowColsChanged = ( export const computeFinalRowCols = ( delta: UIElementSize, - position: XYCoord, + position: XYCord, props: WidgetProps, ): WidgetRowCols | false => { const newRowCols = computeBoundedRowCols( diff --git a/app/client/src/pages/common/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasSelectionArena.tsx index 1aa1fba5fd4b..ba5779ade1d6 100644 --- a/app/client/src/pages/common/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasSelectionArena.tsx @@ -17,7 +17,7 @@ import styled from "styled-components"; import { getNearestParentCanvas } from "utils/generators"; import { useCanvasDragToScroll } from "utils/hooks/useCanvasDragToScroll"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { XYCoord } from "react-dnd"; +import { XYCord } from "utils/hooks/useCanvasDragging"; import { theme } from "constants/DefaultTheme"; const StyledSelectionCanvas = styled.canvas` @@ -120,7 +120,7 @@ export function CanvasSelectionArena({ }; const drawOnEnterObj = useRef<{ canDraw: boolean; - startPoints?: XYCoord; + startPoints?: XYCord; }>(defaultDrawOnObj); // start main container selection from widget editor diff --git a/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts b/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts index 6dd393c3b8e3..862e6c6d2170 100644 --- a/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts +++ b/app/client/src/reducers/uiReducers/canvasSelectionReducer.ts @@ -1,7 +1,7 @@ import { createImmerReducer } from "utils/AppsmithUtils"; import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { XYCoord } from "react-dnd"; +import { XYCord } from "utils/hooks/useCanvasDragging"; const initialState: CanvasSelectionState = { isDraggingForSelection: false, @@ -24,7 +24,7 @@ export const canvasSelectionReducer = createImmerReducer(initialState, { }, [ReduxActionTypes.START_CANVAS_SELECTION_FROM_EDITOR]: ( state: CanvasSelectionState, - action: ReduxAction<{ startPoints?: XYCoord }>, + action: ReduxAction<{ startPoints?: XYCord }>, ) => { state.isDraggingForSelection = true; state.widgetId = MAIN_CONTAINER_WIDGET_ID; @@ -42,7 +42,7 @@ export const canvasSelectionReducer = createImmerReducer(initialState, { export type CanvasSelectionState = { isDraggingForSelection: boolean; widgetId?: string; - outOfCanvasStartPositions?: XYCoord; + outOfCanvasStartPositions?: XYCord; }; export default canvasSelectionReducer; diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index 231a9a313f32..7fdf60e6ff7c 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -1,6 +1,6 @@ import { FetchPageResponse } from "api/PageApi"; import { CANVAS_DEFAULT_HEIGHT_PX } from "constants/AppConstants"; -import { XYCoord } from "react-dnd"; +import { XYCord } from "utils/hooks/useCanvasDragging"; import { ContainerWidgetProps } from "widgets/ContainerWidget"; import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer"; import { @@ -1090,8 +1090,8 @@ export const extractCurrentDSL = ( export const getDropZoneOffsets = ( colWidth: number, rowHeight: number, - dragOffset: XYCoord, - parentOffset: XYCoord, + dragOffset: XYCord, + parentOffset: XYCord, ) => { // Calculate actual drop position by snapping based on x, y and grid cell size return snapToGrid( @@ -1145,10 +1145,10 @@ export const isWidgetOverflowingParentBounds = ( }; export const noCollision = ( - clientOffset: XYCoord, + clientOffset: XYCord, colWidth: number, rowHeight: number, - dropTargetOffset: XYCoord, + dropTargetOffset: XYCord, widgetWidth: number, widgetHeight: number, widgetId: string, @@ -1164,7 +1164,7 @@ export const noCollision = ( const [left, top] = getDropZoneOffsets( colWidth, rowHeight, - clientOffset as XYCoord, + clientOffset as XYCord, dropTargetOffset, ); if (left < 0 || top < 0) { @@ -1203,8 +1203,8 @@ export const currentDropRow = ( export const widgetOperationParams = ( widget: WidgetProps & Partial, - widgetOffset: XYCoord, - parentOffset: XYCoord, + widgetOffset: XYCord, + parentOffset: XYCord, parentColumnSpace: number, parentRowSpace: number, parentWidgetId: string, // parentWidget diff --git a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts index aee212870a0a..bf50987428a6 100644 --- a/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/utils/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -17,7 +17,7 @@ import { widgetOperationParams, } from "utils/WidgetPropsUtils"; import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; -import { XYCoord } from "react-dnd"; +import { XYCord } from "utils/hooks/useCanvasDragging"; import { isEmpty } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasDraggingArena"; import { useDispatch } from "react-redux"; @@ -110,8 +110,8 @@ export const useBlocksToBeDraggedOnCanvas = ({ const getSnappedXY = ( parentColumnWidth: number, parentRowHeight: number, - currentOffset: XYCoord, - parentOffset: XYCoord, + currentOffset: XYCord, + parentOffset: XYCord, ) => { // TODO(abhinav): There is a simpler math to use. const [leftColumn, topRow] = snapToGrid( @@ -278,7 +278,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ { x: bottomMostBlock.left, y: bottomMostBlock.top + bottomMostBlock.height, - } as XYCoord, + } as XYCord, { x: 0, y: 0 }, ); if (top > rows - GridDefaults.CANVAS_EXTENSION_OFFSET) { diff --git a/app/client/src/utils/hooks/useCanvasDragging.ts b/app/client/src/utils/hooks/useCanvasDragging.ts index c429aee7dd88..02a3e834aa9a 100644 --- a/app/client/src/utils/hooks/useCanvasDragging.ts +++ b/app/client/src/utils/hooks/useCanvasDragging.ts @@ -14,6 +14,11 @@ import { } from "./useBlocksToBeDraggedOnCanvas"; import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; +export interface XYCord { + x: number; + y: number; +} + export const useCanvasDragging = ( canvasRef: React.RefObject, canvasDrawRef: React.RefObject,