WIP: Use rows and columns accross the board, cleanup DropTargetMonitor

This commit is contained in:
Abhinav Jha 2019-09-25 22:54:23 +05:30
parent 92eb493d62
commit b155efe3c0
18 changed files with 339 additions and 163 deletions

View File

@ -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,

View File

@ -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<WidgetType, Partial<WidgetProps> & WidgetConfigProps>;
}
class WidgetConfigsApi extends Api {
static url = "/widgetConfigs";
static fetchWidgetConfigs(): Promise<WidgetConfigsResponse> {
return Api.get(WidgetConfigsApi.url);
}
}
export default WidgetConfigsApi;

View File

@ -2,16 +2,21 @@
export const Colors: Record<string, string> = {
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];

View File

@ -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],

View File

@ -8,8 +8,8 @@ import { Color } from "../constants/Colors";
abstract class BaseComponent<T extends ComponentProps> extends Component<T> {}
export interface BaseStyle {
componentHeight?: number;
componentWidth?: number;
componentHeight: number;
componentWidth: number;
positionType: PositionType;
xPosition: number;
yPosition: number;

View File

@ -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 (
<WrappedDragLayer>
<DropZone {...props} currentOffset={currentOffset as XYCoord} />
<DropZone
{...props}
width={widgetWidth}
height={widgetHeight}
currentOffset={currentOffset as XYCoord}
/>
</WrappedDragLayer>
);
};

View File

@ -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<HTMLDivElement | null> = 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<WidgetConfigProps>, 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 (
<WrappedDropTarget
<div
ref={drop}
style={{
position: "relative",
left: props.style.xPosition + props.style.xPositionUnit,
height: props.style.componentHeight,
width: props.style.componentWidth,
top: props.style.yPosition + props.style.yPositionUnit,
}}
>
<DropTargetMask ref={dropTargetMask} />
<DropTargetMask
rowHeight={props.snapRowSpace}
columnWidth={props.snapColumnSpace}
setBounds={handleBoundsUpdate}
/>
<DragLayerComponent
parentOffset={dropTargetTopLeft}
width={DEFAULT_WIDGET_WIDTH}
height={DEFAULT_WIDGET_HEIGHT}
cellSize={DEFAULT_CELL_SIZE}
parentOffset={dropTargetOffset}
parentRowHeight={props.snapRowSpace}
parentColumnWidth={props.snapColumnSpace}
visible={isOver}
/>
{props.children}
</WrappedDropTarget>
</div>
);
};

View File

@ -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<DropTargetMaskProps>`
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<HTMLDivElement | null> = 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 <DropTargetMaskWrapper {...props} ref={dropTargetMask} />;
};
export default DropTargetMask;

View File

@ -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,
};
}
}

View File

@ -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<EditorProps> {
}
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,

View File

@ -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;
};
}

View File

@ -81,7 +81,6 @@ export function* saveLayoutSaga(
) {
try {
const { widgets } = updateLayoutAction.payload;
const denormalizedDSL = CanvasWidgetsNormalizer.denormalize(
Object.keys(widgets)[0],
{ canvasWidgets: widgets },

View File

@ -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 });

View File

@ -19,17 +19,28 @@ import { put, select, takeEvery, takeLatest, all } from "redux-saga/effects";
export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
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<WidgetDelete>) {
export function* moveSaga(moveAction: ReduxAction<WidgetMove>) {
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);

View File

@ -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<WidgetConfigProps>,
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<WidgetProps>,
type: WidgetType,
left: number,
top: number,
width: number,
height: number,
leftColumn: number,
topRow: number,
columns: number,
rows: number,
parentRowSpace: number,
parentColumnSpace: number,
): ContainerWidgetProps<WidgetProps> => {
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,

View File

@ -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),
};
};

View File

@ -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 {

View File

@ -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 (
<DropTargetComponent
{...this.props}
{...this.state}
style={{
...this.getPositionStyle(),
}}