diff --git a/app/client/src/actions/pageActions.tsx b/app/client/src/actions/pageActions.tsx index cc9cfc0675..6196239a3a 100644 --- a/app/client/src/actions/pageActions.tsx +++ b/app/client/src/actions/pageActions.tsx @@ -96,16 +96,18 @@ export const savePageError = (payload: SavePageErrorPayload) => { export type WidgetAddChild = { widgetId: string; type: WidgetType; - left: number; - top: number; - width: number; - height: number; + leftColumn: number; + topRow: number; + columns: number; + rows: number; + parentRowSpace: number; + parentColumnSpace: number; }; export type WidgetMove = { widgetId: string; - left: number; - top: number; + leftColumn: number; + topRow: number; /* If parentWidgetId is different from what we have in redux store, then we have to delete this, diff --git a/app/client/src/api/WidgetConfigsApi.tsx b/app/client/src/api/WidgetConfigsApi.tsx new file mode 100644 index 0000000000..c228e7430b --- /dev/null +++ b/app/client/src/api/WidgetConfigsApi.tsx @@ -0,0 +1,17 @@ +import Api from "./Api"; +import { WidgetType } from "../constants/WidgetConstants"; +import { WidgetProps } from "../widgets/BaseWidget"; +import { WidgetConfigProps } from "../reducers/entityReducers/widgetConfigReducer"; + +export interface WidgetConfigsResponse { + config: Record & WidgetConfigProps>; +} + +class WidgetConfigsApi extends Api { + static url = "/widgetConfigs"; + static fetchWidgetConfigs(): Promise { + return Api.get(WidgetConfigsApi.url); + } +} + +export default WidgetConfigsApi; diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index 8f114112b1..7a4e702fdd 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -2,16 +2,21 @@ export const Colors: Record = { WHITE: "#FFFFFF", POLAR: "#E9FAF3", - GEYSER: "#D0D7DD", + GEYSER: "#D3DEE3", + ATHENS_GRAY: "#FAFBFC", BLACK: "#000000", BLACK_PEARL: "#040627", SHARK: "#21282C", OUTER_SPACE: "#272E32", + SLATE_GRAY: "#768896", + PORCELAIN: "#EBEEF0", + HIT_GRAY: "#A1ACB3", GREEN: "#29CCA3", RED: "#CE4257", PURPLE: "#6871EF", + OXFORD_BLUE: "#2E3D49", }; 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 b2a2709a7b..adf796fe59 100644 --- a/app/client/src/constants/DefaultTheme.tsx +++ b/app/client/src/constants/DefaultTheme.tsx @@ -38,6 +38,7 @@ export const theme: Theme = { border: Colors.GEYSER, paneCard: Colors.SHARK, paneBG: Colors.OUTER_SPACE, + grid: Colors.POLAR, }, lineHeights: [0, 14, 18, 22, 24, 28, 36, 48, 64, 80], fonts: [FontFamilies.DMSans, FontFamilies.AppsmithWidget], diff --git a/app/client/src/editorComponents/BaseComponent.tsx b/app/client/src/editorComponents/BaseComponent.tsx index b4bac3e15f..3389208508 100644 --- a/app/client/src/editorComponents/BaseComponent.tsx +++ b/app/client/src/editorComponents/BaseComponent.tsx @@ -8,8 +8,8 @@ import { Color } from "../constants/Colors"; abstract class BaseComponent extends Component {} export interface BaseStyle { - componentHeight?: number; - componentWidth?: number; + componentHeight: number; + componentWidth: number; positionType: PositionType; xPosition: number; yPosition: number; diff --git a/app/client/src/editorComponents/DragLayerComponent.tsx b/app/client/src/editorComponents/DragLayerComponent.tsx index c390e5f5a6..3c36a871cf 100644 --- a/app/client/src/editorComponents/DragLayerComponent.tsx +++ b/app/client/src/editorComponents/DragLayerComponent.tsx @@ -15,25 +15,38 @@ const WrappedDragLayer = styled.div` `; type DragLayerProps = { - height: number; - width: number; parentOffset: XYCoord; - cellSize: number; + parentRowHeight: number; + parentColumnWidth: number; visible: boolean; }; const DragLayerComponent = (props: DragLayerProps) => { - const { isDragging, currentOffset } = useDragLayer(monitor => ({ + const { isDragging, currentOffset, widget } = useDragLayer(monitor => ({ isDragging: monitor.isDragging(), currentOffset: monitor.getClientOffset(), + widget: monitor.getItem(), })); + 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; + } - if (!isDragging) { + if (!isDragging || !props.visible) { return null; } return ( - + ); }; diff --git a/app/client/src/editorComponents/DropTargetComponent.tsx b/app/client/src/editorComponents/DropTargetComponent.tsx index 0fbefc3f79..74d9b50828 100644 --- a/app/client/src/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/editorComponents/DropTargetComponent.tsx @@ -1,112 +1,109 @@ -import React, { useState, useLayoutEffect, MutableRefObject } from "react"; -import styled from "styled-components"; -import { WidgetProps, WidgetOperations } from "../widgets/BaseWidget"; -import { useDrop } from "react-dnd"; +import React, { useState } from "react"; +import { WidgetProps } from "../widgets/BaseWidget"; +import { WidgetConfigProps } from "../reducers/entityReducers/widgetConfigReducer"; +import { useDrop, XYCoord } from "react-dnd"; import { ContainerProps } from "./ContainerComponent"; import WidgetFactory from "../utils/WidgetFactory"; -import { snapToGrid } from "../utils/helpers"; + +import { widgetOperationParams } from "../utils/WidgetPropsUtils"; import DragLayerComponent from "./DragLayerComponent"; +import DropTargetMask from "./DropTargetMask"; -import { GridDefaults } from "../constants/WidgetConstants"; - -const { - DEFAULT_CELL_SIZE, - DEFAULT_WIDGET_HEIGHT, - DEFAULT_WIDGET_WIDTH, -} = GridDefaults; +/*TODO: + - Try to keep only component props, state and drop hook here - DONE + - Move all child components to their own file - DONE + - Provide Draglayer with the actual component size if exists + - else pull it from widgetConfig - DONE + - Provide Draglayer with rows, columns, rowHeight, columnWidth instead of width height pixels - DONE + - Return rows and columns to the drop handler (updateWidget) - DONE + - Update WidgetOperations to handle rows and columns + - Increase default canvas rowHeight + - Fix child container positioning +*/ type DropTargetComponentProps = ContainerProps & { updateWidget?: Function; + snapColumns?: number; + snapRows?: number; + snapColumnSpace: number; + snapRowSpace: number; }; -const WrappedDropTarget = styled.div` - background: white; -`; -const DropTargetMask = styled.div` - position: absolute; - z-index: -10; - left: 0; - right: 0; -`; +type DropTargetBounds = { + x: number; + y: number; + width: number; + height: number; +}; export const DropTargetComponent = (props: DropTargetComponentProps) => { - const [dropTargetTopLeft, setDropTargetTopLeft] = useState({ x: 0, y: 0 }); - const dropTargetMask: MutableRefObject = React.useRef( - null, - ); - useLayoutEffect(() => { - const el = dropTargetMask.current; - if (el) { - const rect = el.getBoundingClientRect(); - setDropTargetTopLeft({ - x: rect.left, - y: rect.top, - }); - } - }, [setDropTargetTopLeft]); + // Hook to keep the bounds of the drop target container in state + const [dropTargetOffset, setDropTargetOffset] = useState({ x: 0, y: 0 }); + // Make this component a drop target const [{ isOver }, drop] = useDrop({ accept: Object.values(WidgetFactory.getWidgetTypes()), - drop(widget: WidgetProps, monitor) { - if (monitor.isOver({ shallow: true })) { - const clientOffset = monitor.getClientOffset(); - if (clientOffset) { - const [x, y] = snapToGrid( - DEFAULT_CELL_SIZE, - clientOffset.x - dropTargetTopLeft.x, - clientOffset.y - dropTargetTopLeft.y, + drop(widget: WidgetProps & Partial, monitor) { + // Make sure we're dropping in this container. + if (isOver) { + props.updateWidget && + props.updateWidget( + ...widgetOperationParams( + widget, + monitor.getClientOffset() as XYCoord, + dropTargetOffset, + props.snapColumnSpace, + props.snapRowSpace, + props.widgetId, + ), ); - if (widget.widgetId) { - props.updateWidget && - props.updateWidget(WidgetOperations.MOVE, widget.widgetId, { - left: x, - top: y, - }); - } else { - props.updateWidget && - props.updateWidget(WidgetOperations.ADD_CHILD, props.widgetId, { - type: widget.type, - left: x, - top: y, - width: - Math.round(DEFAULT_WIDGET_WIDTH / DEFAULT_CELL_SIZE) * - DEFAULT_CELL_SIZE, - height: - Math.round(DEFAULT_WIDGET_HEIGHT / DEFAULT_CELL_SIZE) * - DEFAULT_CELL_SIZE, - }); - } - } } return undefined; }, + // Collect isOver for ui transforms when hovering over this component collect: monitor => ({ isOver: !!monitor.isOver({ shallow: true }), }), + // 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 canDrop: (widget, monitor) => { return monitor.isOver({ shallow: true }); }, }); + + const handleBoundsUpdate = (rect: DOMRect) => { + if (rect.x !== dropTargetOffset.x || rect.y !== dropTargetOffset.y) { + setDropTargetOffset({ + x: rect.x, + y: rect.y, + }); + } + }; + return ( - - + {props.children} - + ); }; diff --git a/app/client/src/editorComponents/DropTargetMask.tsx b/app/client/src/editorComponents/DropTargetMask.tsx new file mode 100644 index 0000000000..dba29bf90e --- /dev/null +++ b/app/client/src/editorComponents/DropTargetMask.tsx @@ -0,0 +1,45 @@ +import React, { useLayoutEffect, MutableRefObject } from "react"; +import styled from "styled-components"; + +type DropTargetMaskProps = { + rowHeight: number; + columnWidth: number; + setBounds: Function; +}; + +export const DropTargetMaskWrapper = styled.div` + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 0; + width: 100%; + height: 100%; + background: white; + background-image: radial-gradient( + circle, + ${props => props.theme.colors.grid} 2px, + transparent 0 + ); + background-size: ${props => props.columnWidth}px ${props => props.rowHeight}px; + background-position: -50% -50%; +`; +/* eslint-disable react/display-name */ +export const DropTargetMask = (props: DropTargetMaskProps) => { + // An underlay div for Grid markers and calculating the width, height, x and y positions + const dropTargetMask: MutableRefObject = React.useRef( + null, + ); + // Fetch new height, width, x and y positions + useLayoutEffect(() => { + const el = dropTargetMask.current; + if (el) { + const rect = el.getBoundingClientRect(); + props.setBounds(rect); + } + }); + + return ; +}; + +export default DropTargetMask; diff --git a/app/client/src/editorComponents/Dropzone.tsx b/app/client/src/editorComponents/Dropzone.tsx index b441232eba..f4b0f93959 100644 --- a/app/client/src/editorComponents/Dropzone.tsx +++ b/app/client/src/editorComponents/Dropzone.tsx @@ -24,7 +24,8 @@ type DropZoneProps = { width: number; visible: boolean; parentOffset: XYCoord; - cellSize: number; + parentRowHeight: number; + parentColumnWidth: number; }; /* eslint-disable react/display-name */ @@ -39,17 +40,18 @@ export const DropZone = (props: DropZoneProps) => { if (props.visible) { if (props.currentOffset && props.currentOffset.x >= props.parentOffset.x) { - const [x, y] = snapToGrid( - props.cellSize, + const [leftColumn, topRow] = snapToGrid( + props.parentColumnWidth, + props.parentRowHeight, props.currentOffset.x - props.parentOffset.x, props.currentOffset.y - props.parentOffset.y, ); wrapperProps = { visible: true, - left: x, - top: y, - height: props.height, - width: props.width, + left: leftColumn * props.parentColumnWidth, + top: topRow * props.parentRowHeight, + height: props.height * props.parentRowHeight, + width: props.width * props.parentColumnWidth, }; } } diff --git a/app/client/src/pages/Editor/index.tsx b/app/client/src/pages/Editor/index.tsx index 2c048803d8..b60e615fad 100644 --- a/app/client/src/pages/Editor/index.tsx +++ b/app/client/src/pages/Editor/index.tsx @@ -28,7 +28,7 @@ const CanvasContainer = styled.section` height: 100%; width: 100%; position: relative; - overflow-x: hidden; + overflow-x: auto; overflow-y: auto; margin: 0px 10px; &:before { @@ -109,12 +109,29 @@ class Editor extends Component { } const mapStateToProps = (state: AppState): EditorReduxState => { + // TODO(abhinav) : Benchmark this, see how many times this is called in the application + // lifecycle. Move to using flattend redux state for widgets if necessary. + + // Also, try to merge the widgetCards and widgetConfigs in the fetch Saga. + // No point in storing widgetCards, without widgetConfig + // Alternatively, try to see if we can continue to use only WidgetConfig and eliminate WidgetCards + const dsl = CanvasWidgetsNormalizer.denormalize( state.ui.editor.pageWidgetId, state.entities, ); + const configs = state.entities.widgetConfig.config; + + const cards = state.ui.widgetCardsPane.cards; + Object.keys(cards).forEach((group: string) => { + cards[group] = cards[group].map((widget: WidgetCardProps) => ({ + ...widget, + ...configs[widget.type], + })); + }); + return { - cards: state.ui.widgetCardsPane.cards, + cards, dsl, pageWidgetId: state.ui.editor.pageWidgetId, currentPageId: state.ui.editor.currentPageId, diff --git a/app/client/src/reducers/index.tsx b/app/client/src/reducers/index.tsx index 83d74479bf..4483e76eca 100644 --- a/app/client/src/reducers/index.tsx +++ b/app/client/src/reducers/index.tsx @@ -9,6 +9,7 @@ import { QueryDataState } from "./entityReducers/queryDataReducer"; import { ActionDataState } from "./entityReducers/actionsReducer"; import { PropertyPaneConfigState } from "./entityReducers/propertyPaneConfigReducer"; import { PropertyPaneReduxState } from "./uiReducers/propertyPaneReducer"; +import { WidgetConfigReducerState } from "./entityReducers/widgetConfigReducer"; const appReducer = combineReducers({ entities: entityReducer, @@ -29,5 +30,6 @@ export interface AppState { queryData: QueryDataState; actions: ActionDataState; propertyConfig: PropertyPaneConfigState; + widgetConfig: WidgetConfigReducerState; }; } diff --git a/app/client/src/sagas/PageSagas.tsx b/app/client/src/sagas/PageSagas.tsx index b86c4ebf06..8e25043526 100644 --- a/app/client/src/sagas/PageSagas.tsx +++ b/app/client/src/sagas/PageSagas.tsx @@ -81,7 +81,6 @@ export function* saveLayoutSaga( ) { try { const { widgets } = updateLayoutAction.payload; - const denormalizedDSL = CanvasWidgetsNormalizer.denormalize( Object.keys(widgets)[0], { canvasWidgets: widgets }, diff --git a/app/client/src/sagas/WidgetCardsPaneSagas.tsx b/app/client/src/sagas/WidgetCardsPaneSagas.tsx index ca7ef6b732..d6cf112b1a 100644 --- a/app/client/src/sagas/WidgetCardsPaneSagas.tsx +++ b/app/client/src/sagas/WidgetCardsPaneSagas.tsx @@ -4,13 +4,13 @@ import WidgetCardsPaneApi, { WidgetCardsPaneResponse, } from "../api/WidgetCardsPaneApi"; import { successFetchingWidgetCards } from "../actions/widgetCardsPaneActions"; -import { call, put, takeLatest } from "redux-saga/effects"; +import { call, put, takeLatest, all } from "redux-saga/effects"; export function* fetchWidgetCards() { try { - const widgetCards: WidgetCardsPaneResponse = yield call( - WidgetCardsPaneApi.fetchWidgetCards, - ); + const widgetCards: WidgetCardsPaneResponse = yield all([ + call(WidgetCardsPaneApi.fetchWidgetCards), + ]); yield put(successFetchingWidgetCards(widgetCards.cards)); } catch (err) { yield put({ type: ReduxActionTypes.ERROR_FETCHING_WIDGET_CARDS, err }); diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 715f8e065c..e870a391ae 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -19,17 +19,28 @@ import { put, select, takeEvery, takeLatest, all } from "redux-saga/effects"; export function* addChildSaga(addChildAction: ReduxAction) { try { - const { widgetId, type, left, top, width, height } = addChildAction.payload; + const { + widgetId, + type, + leftColumn, + topRow, + columns, + rows, + parentRowSpace, + parentColumnSpace, + } = addChildAction.payload; const widget: FlattenedWidgetProps = yield select(getWidget, widgetId); const widgets = yield select(getWidgets); const childWidget = generateWidgetProps( widget, type, - left, - top, - width, - height, + leftColumn, + topRow, + columns, + rows, + parentRowSpace, + parentColumnSpace, ); widgets[childWidget.widgetId] = childWidget; if (widget && widget.children) { @@ -81,14 +92,14 @@ export function* deleteSaga(deleteAction: ReduxAction) { export function* moveSaga(moveAction: ReduxAction) { try { - const { widgetId, left, top, parentWidgetId } = moveAction.payload; + const { widgetId, leftColumn, topRow, parentWidgetId } = moveAction.payload; let widget: FlattenedWidgetProps = yield select(getWidget, widgetId); const widgets = yield select(getWidgets) as any; let parentWidget = null; if (parentWidgetId) { parentWidget = yield select(getWidget, parentWidgetId); } - widget = updateWidgetPosition(widget, left, top, parentWidget); + widget = updateWidgetPosition(widget, leftColumn, topRow, parentWidget); widgets[widgetId] = widget; if (parentWidgetId) { widgets[parentWidgetId].children.push(widgetId); diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index 8b35bf179a..f95859f867 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -1,10 +1,13 @@ import { FetchPageResponse } from "../api/PageApi"; +import { XYCoord } from "react-dnd"; import { ContainerWidgetProps } from "../widgets/ContainerWidget"; -import { WidgetProps } from "../widgets/BaseWidget"; +import { WidgetConfigProps } from "../reducers/entityReducers/widgetConfigReducer"; +import { WidgetProps, WidgetOperations } from "../widgets/BaseWidget"; import { WidgetType, RenderModes } from "../constants/WidgetConstants"; import { generateReactKey } from "../utils/generators"; import { Colors } from "../constants/Colors"; import { GridDefaults, WidgetTypes } from "../constants/WidgetConstants"; +import { snapToGrid } from "./helpers"; const { DEFAULT_GRID_COLUMNS, DEFAULT_GRID_ROWS } = GridDefaults; @@ -14,21 +17,67 @@ export const extractCurrentDSL = ( return fetchPageResponse.data.layouts[0].dsl; }; +export const widgetOperationParams = ( + widget: WidgetProps & Partial, + widgetOffset: XYCoord, + parentOffset: XYCoord, + parentColumnSpace: number, + parentRowSpace: number, + widgetId?: string, +) => { + if (widgetOffset) { + // Calculate actual drop position by snapping based on x, y and grid cell size + const [leftColumn, topRow] = snapToGrid( + parentColumnSpace, + parentRowSpace, + widgetOffset.x - parentOffset.x, + widgetOffset.y - parentOffset.y, + ); + // If this is an existing widget, we'll have the widgetId + // Therefore, this is a move operation on drop of the widget + if (widget.widgetId) { + return [ + WidgetOperations.MOVE, + widget.widgetId, + { + leftColumn, + topRow, + }, + ]; + // If this is not an existing widget, we'll not have the widgetId + // Therefore, this is an operation to add child to this container + } else { + const widgetDimensions = { + columns: widget.columns, + rows: widget.rows, + }; + return [ + WidgetOperations.ADD_CHILD, + widgetId, + { + type: widget.type, + leftColumn, + topRow, + ...widgetDimensions, + parentRowSpace, + parentColumnSpace, + }, + ]; + } + } +}; + export const updateWidgetPosition = ( widget: WidgetProps, - left: number, - top: number, + leftColumn: number, + topRow: number, parent?: WidgetProps, ) => { const newPositions = { - leftColumn: Math.floor(left / widget.parentColumnSpace), - topRow: Math.floor(top / widget.parentRowSpace), - rightColumn: - Math.floor(left / widget.parentColumnSpace) + - (widget.rightColumn - widget.leftColumn), - bottomRow: - Math.floor(top / widget.parentRowSpace) + - (widget.bottomRow - widget.topRow), + leftColumn, + topRow, + rightColumn: leftColumn + (widget.rightColumn - widget.leftColumn), + bottomRow: topRow + (widget.bottomRow - widget.topRow), }; if (parent) { } @@ -60,25 +109,19 @@ export const updateWidgetSize = ( export const generateWidgetProps = ( parent: ContainerWidgetProps, type: WidgetType, - left: number, - top: number, - width: number, - height: number, + leftColumn: number, + topRow: number, + columns: number, + rows: number, + parentRowSpace: number, + parentColumnSpace: number, ): ContainerWidgetProps => { if (parent && parent.snapColumns && parent.snapRows) { - const parentColumnWidth = Math.floor( - ((parent.rightColumn - parent.leftColumn) * parent.parentColumnSpace) / - parent.snapColumns, - ); - const parentRowHeight = Math.floor( - ((parent.bottomRow - parent.topRow) * parent.parentRowSpace) / - parent.snapRows, - ); const sizes = { - leftColumn: Math.floor(left / parentColumnWidth), - rightColumn: Math.floor((left + width) / parentColumnWidth), - topRow: Math.floor(top / parentRowHeight), - bottomRow: Math.floor((top + height) / parentRowHeight), + leftColumn, + rightColumn: leftColumn + columns, + topRow, + bottomRow: topRow + rows, }; let others = {}; if (type === WidgetTypes.CONTAINER_WIDGET) { @@ -96,8 +139,8 @@ export const generateWidgetProps = ( widgetId: generateReactKey(), widgetName: generateReactKey(), //TODO: figure out what this is to populate appropriately isVisible: true, - parentColumnSpace: parentColumnWidth, - parentRowSpace: parentRowHeight, + parentColumnSpace, + parentRowSpace, renderMode: RenderModes.CANVAS, //Is this required? ...sizes, ...others, diff --git a/app/client/src/utils/helpers.tsx b/app/client/src/utils/helpers.tsx index 6d8128797b..a5a5c72486 100644 --- a/app/client/src/utils/helpers.tsx +++ b/app/client/src/utils/helpers.tsx @@ -1,5 +1,22 @@ -export const snapToGrid = (cellSize: number, x: number, y: number) => { - const snappedX = Math.floor(x / cellSize) * cellSize; - const snappedY = Math.floor(y / cellSize) * cellSize; +export const snapToGrid = ( + columnWidth: number, + rowHeight: number, + x: number, + y: number, +) => { + const snappedX = Math.floor(x / columnWidth); + const snappedY = Math.floor(y / rowHeight); return [snappedX, snappedY]; }; + +export const getRowColSizes = ( + rowCount: number, + columnCount: number, + width: number, + height: number, +): { rowHeight: number; columnWidth: number } => { + return { + columnWidth: Math.floor(width / columnCount), + rowHeight: Math.floor(height / rowCount), + }; +}; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index f4bd1f8729..c60c102542 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -24,11 +24,11 @@ abstract class BaseWidget< constructor(props: T) { super(props); const initialState: WidgetState = { - height: 0, - width: 0, + componentHeight: 0, + componentWidth: 0, }; - initialState.height = 0; - initialState.width = 0; + initialState.componentHeight = 0; + initialState.componentWidth = 0; this.state = initialState as K; } @@ -63,13 +63,13 @@ abstract class BaseWidget< parentRowSpace: number, ) { const widgetState: WidgetState = { - width: (rightColumn - leftColumn) * parentColumnSpace, - height: (bottomRow - topRow) * parentRowSpace, + componentWidth: (rightColumn - leftColumn) * parentColumnSpace, + componentHeight: (bottomRow - topRow) * parentRowSpace, }; if ( _.isNil(this.state) || - widgetState.height !== this.state.height || - widgetState.width !== this.state.width + widgetState.componentHeight !== this.state.componentHeight || + widgetState.componentWidth !== this.state.componentWidth ) { this.setState(widgetState); } @@ -112,8 +112,8 @@ abstract class BaseWidget< getPositionStyle(): BaseStyle { return { positionType: "CONTAINER_DIRECTION", - componentHeight: this.state.height, - componentWidth: this.state.width, + componentHeight: this.state.componentHeight, + componentWidth: this.state.componentWidth, yPosition: this.props.topRow * this.props.parentRowSpace, xPosition: this.props.leftColumn * this.props.parentColumnSpace, xPositionUnit: CSSUnits.PIXEL, @@ -130,8 +130,8 @@ abstract class BaseWidget< } export interface WidgetState { - height: number; - width: number; + componentHeight: number; + componentWidth: number; } export interface DraggableWidget { @@ -160,6 +160,7 @@ export interface WidgetDataProps { parentColumnSpace: number; parentRowSpace: number; isVisible?: boolean; + isRoot?: boolean; } export interface WidgetFunctions { diff --git a/app/client/src/widgets/ContainerWidget.tsx b/app/client/src/widgets/ContainerWidget.tsx index 85373a8e0e..32fca51313 100644 --- a/app/client/src/widgets/ContainerWidget.tsx +++ b/app/client/src/widgets/ContainerWidget.tsx @@ -22,10 +22,10 @@ class ContainerWidget extends BaseWidget< super(props); this.renderChildWidget = this.renderChildWidget.bind(this); this.state = { - width: 0, - height: 0, - snapColumnSpace: DEFAULT_GRID_COLUMNS, - snapRowSpace: DEFAULT_GRID_ROWS, + componentWidth: 0, + componentHeight: 0, + snapColumnSpace: 0, + snapRowSpace: 0, }; } @@ -33,19 +33,22 @@ class ContainerWidget extends BaseWidget< super.componentDidUpdate(previousProps); let snapColumnSpace = this.state.snapColumnSpace; let snapRowSpace = this.state.snapRowSpace; - if (this.state.width) - snapColumnSpace = - this.state.width / (this.props.snapColumns || DEFAULT_GRID_COLUMNS); - if (this.state.height) - snapRowSpace = - this.state.height / (this.props.snapRows || DEFAULT_GRID_ROWS); + if (this.state.componentWidth) + snapColumnSpace = Math.floor( + this.state.componentWidth / + (this.props.snapColumns || DEFAULT_GRID_COLUMNS), + ); + if (this.state.componentHeight) + snapRowSpace = Math.floor( + this.state.componentHeight / (this.props.snapRows || DEFAULT_GRID_ROWS), + ); if ( this.state.snapColumnSpace !== snapColumnSpace || this.state.snapRowSpace !== snapRowSpace ) { this.setState({ - snapColumnSpace: snapColumnSpace, - snapRowSpace: snapRowSpace, + snapColumnSpace, + snapRowSpace, }); } } @@ -79,6 +82,7 @@ class ContainerWidget extends BaseWidget< return (