Cleanup
DragLayerComponent
This commit is contained in:
parent
af8b400c05
commit
99d660370d
|
|
@ -7,6 +7,7 @@ import {
|
|||
} from "constants/ApiConstants";
|
||||
import { ActionApiResponse } from "./ActionAPI";
|
||||
import { AUTH_LOGIN_URL } from "constants/routes";
|
||||
import { setRouteBeforeLogin } from "utils/storage";
|
||||
const { apiUrl, baseUrl } = getAppsmithConfigs();
|
||||
|
||||
//TODO(abhinav): Refactor this to make more composable.
|
||||
|
|
@ -41,6 +42,9 @@ axiosInstance.interceptors.response.use(
|
|||
return response.data;
|
||||
},
|
||||
function(error: any) {
|
||||
if (error.code === "ECONNABORTED") {
|
||||
console.log("CONNECTION TIMEOUT");
|
||||
}
|
||||
if (error.config.url.match(executeActionRegex)) {
|
||||
return makeExecuteActionResponse(error.response);
|
||||
}
|
||||
|
|
@ -51,6 +55,7 @@ axiosInstance.interceptors.response.use(
|
|||
// console.log(error.response.status);
|
||||
// console.log(error.response.headers);
|
||||
if (error.response.status === 401) {
|
||||
setRouteBeforeLogin(window.location.pathname);
|
||||
window.location.href = AUTH_LOGIN_URL;
|
||||
}
|
||||
return Promise.reject(error.response.data);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ const StyledContainerComponent = styled.div<ContainerComponentProps>`
|
|||
props.containerStyle !== "none"
|
||||
? `
|
||||
border: ${getBorderCSSShorthand(props.theme.borders[2])};
|
||||
box-shadow: ${props.theme.shadows[0]};
|
||||
border-radius: ${
|
||||
props.containerStyle === "card" || props.containerStyle === "rounded-border"
|
||||
? props.theme.radii[1]
|
||||
|
|
@ -19,7 +18,7 @@ const StyledContainerComponent = styled.div<ContainerComponentProps>`
|
|||
height: 100%;
|
||||
width: 100%;
|
||||
background: ${props => props.backgroundColor};
|
||||
padding: ${props => props.theme.spaces[1]}px;
|
||||
position: relative;
|
||||
}`;
|
||||
|
||||
/* eslint-disable react/display-name */
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import React from "react";
|
||||
import { BaseStyle } from "widgets/BaseWidget";
|
||||
import { PositionTypes } from "constants/WidgetConstants";
|
||||
import { PositionTypes, WIDGET_PADDING } from "constants/WidgetConstants";
|
||||
import { theme } from "constants/DefaultTheme";
|
||||
|
||||
type PositionedContainerProps = {
|
||||
style: BaseStyle;
|
||||
children: JSX.Element | JSX.Element[];
|
||||
isMainContainer?: boolean;
|
||||
};
|
||||
|
||||
export const PositionedContainer = (props: PositionedContainerProps) => {
|
||||
|
|
@ -19,8 +19,10 @@ export const PositionedContainer = (props: PositionedContainerProps) => {
|
|||
height: props.style.componentHeight + (props.style.heightUnit || "px"),
|
||||
width: props.style.componentWidth + (props.style.widthUnit || "px"),
|
||||
left: props.style.xPosition + (props.style.xPositionUnit || "px"),
|
||||
top: props.style.yPosition + (props.style.yPositionUnit || "px"),
|
||||
padding: PositionedContainer.padding + "px",
|
||||
top: props.isMainContainer
|
||||
? theme.spaces[9]
|
||||
: props.style.yPosition + (props.style.yPositionUnit || "px"),
|
||||
padding: props.isMainContainer ? 0 : WIDGET_PADDING + "px",
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
|
|
@ -28,6 +30,6 @@ export const PositionedContainer = (props: PositionedContainerProps) => {
|
|||
);
|
||||
};
|
||||
|
||||
PositionedContainer.padding = theme.spaces[2];
|
||||
PositionedContainer.padding = WIDGET_PADDING;
|
||||
|
||||
export default PositionedContainer;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
import React from "react";
|
||||
import React, { useContext } from "react";
|
||||
import styled from "styled-components";
|
||||
import { useDragLayer, XYCoord } from "react-dnd";
|
||||
import DropZone from "./Dropzone";
|
||||
import { noCollision } from "utils/WidgetPropsUtils";
|
||||
import { noCollision, currentDropRow } from "utils/WidgetPropsUtils";
|
||||
import { OccupiedSpace } from "constants/editorConstants";
|
||||
import DropTargetMask from "./DropTargetMask";
|
||||
|
||||
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
|
||||
import { DropTargetContext } from "./DropTargetComponent";
|
||||
const WrappedDragLayer = styled.div`
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
type DragLayerProps = {
|
||||
|
|
@ -27,9 +28,11 @@ type DragLayerProps = {
|
|||
parentRows?: number;
|
||||
parentCols?: number;
|
||||
isResizing?: boolean;
|
||||
parentWidgetId: string;
|
||||
};
|
||||
|
||||
const DragLayerComponent = (props: DragLayerProps) => {
|
||||
const { updateDropTargetRows } = useContext(DropTargetContext);
|
||||
const { isDragging, currentOffset, widget, canDrop } = useDragLayer(
|
||||
monitor => ({
|
||||
isDragging: monitor.isDragging(),
|
||||
|
|
@ -48,6 +51,22 @@ const DragLayerComponent = (props: DragLayerProps) => {
|
|||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
props.visible &&
|
||||
props.parentWidgetId === MAIN_CONTAINER_WIDGET_ID &&
|
||||
currentOffset &&
|
||||
props.parentRows
|
||||
) {
|
||||
const row = currentDropRow(
|
||||
props.parentRowHeight,
|
||||
props.parentOffset.y,
|
||||
currentOffset.y,
|
||||
widget,
|
||||
);
|
||||
|
||||
updateDropTargetRows && updateDropTargetRows(row);
|
||||
}
|
||||
|
||||
let widgetWidth = 0;
|
||||
let widgetHeight = 0;
|
||||
if (widget) {
|
||||
|
|
@ -59,14 +78,15 @@ const DragLayerComponent = (props: DragLayerProps) => {
|
|||
if ((!isDragging || !props.visible) && !props.isResizing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<WrappedDragLayer>
|
||||
<DropTargetMask
|
||||
rowHeight={props.parentRowHeight}
|
||||
columnWidth={props.parentColumnWidth}
|
||||
setBounds={props.onBoundsUpdate}
|
||||
showGrid={(isDragging && props.isOver) || props.isResizing}
|
||||
/>
|
||||
|
||||
<DropZone
|
||||
{...props}
|
||||
visible={props.visible && props.isOver}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ const DraggableWrapper = styled.div<{ show: boolean }>`
|
|||
const WidgetBoundaries = styled.div`
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px dashed
|
||||
|
|
@ -160,6 +160,13 @@ const DraggableComponent = (props: DraggableComponentProps) => {
|
|||
},
|
||||
});
|
||||
|
||||
let stackingContext = 0;
|
||||
if (props.widgetId === selectedWidget) {
|
||||
stackingContext = 1;
|
||||
}
|
||||
if (props.widgetId === focusedWidget) {
|
||||
stackingContext = 2;
|
||||
}
|
||||
const isResizingOrDragging =
|
||||
selectedWidget !== props.widgetId && (!!isResizing || !!isDragging);
|
||||
|
||||
|
|
@ -207,13 +214,11 @@ const DraggableComponent = (props: DraggableComponentProps) => {
|
|||
height: "100%",
|
||||
userSelect: "none",
|
||||
cursor: "drag",
|
||||
zIndex: props.widgetId === selectedWidget ? 3 : 1,
|
||||
zIndex: stackingContext,
|
||||
}}
|
||||
>
|
||||
<WidgetBoundaries
|
||||
style={{ display: isResizingOrDragging ? "block" : "none" }}
|
||||
/>
|
||||
{props.children}
|
||||
|
||||
<DragHandle className="control" ref={drag}>
|
||||
<Tooltip content="Move" hoverOpenDelay={500}>
|
||||
{moveControlIcon}
|
||||
|
|
@ -229,6 +234,9 @@ const DraggableComponent = (props: DraggableComponentProps) => {
|
|||
{editControlIcon}
|
||||
</Tooltip>
|
||||
</EditControl>
|
||||
<WidgetBoundaries
|
||||
style={{ display: isResizingOrDragging ? "block" : "none" }}
|
||||
/>
|
||||
</DraggableWrapper>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
import React, { useState, useContext, ReactNode } from "react";
|
||||
import React, {
|
||||
useState,
|
||||
useContext,
|
||||
ReactNode,
|
||||
Context,
|
||||
createContext,
|
||||
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";
|
||||
|
|
@ -6,8 +14,15 @@ import WidgetFactory from "utils/WidgetFactory";
|
|||
import { widgetOperationParams, noCollision } from "utils/WidgetPropsUtils";
|
||||
import { EditorContext } from "components/editorComponents/EditorContextProvider";
|
||||
import { FocusContext, DragResizeContext } from "pages/Editor/CanvasContexts";
|
||||
|
||||
import {
|
||||
MAIN_CONTAINER_WIDGET_ID,
|
||||
WIDGET_PADDING,
|
||||
} from "constants/WidgetConstants";
|
||||
import { calculateDropTargetRows } from "./DropTargetUtils";
|
||||
import DragLayerComponent from "./DragLayerComponent";
|
||||
import { AppState } from "reducers";
|
||||
import { useSelector } from "react-redux";
|
||||
import { theme } from "constants/DefaultTheme";
|
||||
|
||||
type DropTargetComponentProps = WidgetProps & {
|
||||
children?: ReactNode;
|
||||
|
|
@ -22,18 +37,99 @@ type DropTargetBounds = {
|
|||
height: number;
|
||||
};
|
||||
|
||||
const StyledDropTarget = styled.div`
|
||||
transition: height 100ms ease-in;
|
||||
`;
|
||||
|
||||
/*
|
||||
This context will provide the function which will help the draglayer and resizablecomponents trigger
|
||||
an update of the main container's rows
|
||||
*/
|
||||
export const DropTargetContext: Context<{
|
||||
updateDropTargetRows?: (row: number) => boolean;
|
||||
persistDropTargetRows?: (widgetId: string, rows: number) => void;
|
||||
}> = createContext({});
|
||||
|
||||
export const DropTargetComponent = (props: DropTargetComponentProps) => {
|
||||
// Hook to keep the offset of the drop target container in state
|
||||
const [dropTargetOffset, setDropTargetOffset] = useState({ x: 0, y: 0 });
|
||||
const { updateWidget, occupiedSpaces } = useContext(EditorContext);
|
||||
const { selectWidget, showPropertyPane } = useContext(FocusContext);
|
||||
const [rows, setRows] = useState(props.snapRows);
|
||||
useEffect(() => {
|
||||
setRows(props.snapRows);
|
||||
}, [props.snapRows]);
|
||||
const { updateWidget, occupiedSpaces, updateWidgetProperty } = useContext(
|
||||
EditorContext,
|
||||
);
|
||||
const { selectWidget, showPropertyPane, selectedWidget } = useContext(
|
||||
FocusContext,
|
||||
);
|
||||
const { isResizing } = useContext(DragResizeContext);
|
||||
|
||||
const spacesOccupiedBySiblingWidgets =
|
||||
occupiedSpaces && occupiedSpaces[props.widgetId]
|
||||
? occupiedSpaces[props.widgetId]
|
||||
: undefined;
|
||||
|
||||
const childWidgets = useSelector(
|
||||
(state: AppState) => state.entities.canvasWidgets[props.widgetId].children,
|
||||
);
|
||||
|
||||
const persistDropTargetRows = (widgetId: string, widgetBottomRow: number) => {
|
||||
if (props.widgetId === MAIN_CONTAINER_WIDGET_ID) {
|
||||
const occupiedSpacesByChildren =
|
||||
occupiedSpaces && occupiedSpaces[MAIN_CONTAINER_WIDGET_ID];
|
||||
|
||||
const rowsToPersist = calculateDropTargetRows(
|
||||
widgetId,
|
||||
widgetBottomRow,
|
||||
rows,
|
||||
occupiedSpacesByChildren,
|
||||
);
|
||||
setRows(rowsToPersist);
|
||||
|
||||
/* Update the main container's rows, ONLY if it has changed since the last render */
|
||||
if (props.snapRows !== rowsToPersist) {
|
||||
updateWidgetProperty &&
|
||||
updateWidgetProperty(props.widgetId, "snapRows", rowsToPersist);
|
||||
updateWidgetProperty &&
|
||||
updateWidgetProperty(
|
||||
props.widgetId,
|
||||
"bottomRow",
|
||||
Math.round(
|
||||
(rowsToPersist * props.snapRowSpace) / props.parentRowSpace,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Update the rows of the main container based on the current widget's (dragging/resizing) bottom row */
|
||||
const updateDropTargetRows = (widgetBottomRow: number) => {
|
||||
if (props.widgetId === MAIN_CONTAINER_WIDGET_ID) {
|
||||
/* If the widget has reached the penultimate row of the main container */
|
||||
if (widgetBottomRow > rows - 1) {
|
||||
setRows(rows + 2);
|
||||
return true;
|
||||
// If the current widget's (dragging/resizing) bottom row has moved back up
|
||||
} else if (widgetBottomRow < rows - 2 && rows - props.snapRows >= 2) {
|
||||
setRows(rows - 2);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const isChildFocused =
|
||||
!!childWidgets &&
|
||||
!!selectedWidget &&
|
||||
childWidgets.length > 0 &&
|
||||
childWidgets.indexOf(selectedWidget) > -1;
|
||||
|
||||
const isChildResizing = !!isResizing && isChildFocused;
|
||||
// Make this component a drop target
|
||||
const [{ isOver, isExactlyOver }, drop] = useDrop({
|
||||
const [{ isExactlyOver }, drop] = useDrop({
|
||||
accept: Object.values(WidgetFactory.getWidgetTypes()),
|
||||
drop(widget: WidgetProps & Partial<WidgetConfigProps>, monitor) {
|
||||
// Make sure we're dropping in this container.
|
||||
|
|
@ -46,16 +142,30 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
|
|||
props.snapRowSpace,
|
||||
props.widgetId,
|
||||
);
|
||||
|
||||
// Only show propertypane if this is a new widget.
|
||||
// If it is not a new widget, then let the DraggableComponent handle it.
|
||||
showPropertyPane &&
|
||||
updateWidgetParams.payload.newWidgetId &&
|
||||
showPropertyPane(updateWidgetParams.payload.newWidgetId);
|
||||
|
||||
// Select the widget if it is a new widget
|
||||
selectWidget &&
|
||||
updateWidgetParams.payload.newWidgetId &&
|
||||
selectWidget(updateWidgetParams.payload.newWidgetId);
|
||||
|
||||
/* currently dropped widget's bottom row */
|
||||
const droppedWidgetBottomRow = updateWidgetParams.payload.rows
|
||||
? updateWidgetParams.payload.topRow + updateWidgetParams.payload.rows
|
||||
: updateWidgetParams.payload.topRow +
|
||||
(widget.bottomRow - widget.topRow);
|
||||
|
||||
persistDropTargetRows(
|
||||
widget.widgetId || updateWidgetParams.payload.newWidgetId,
|
||||
droppedWidgetBottomRow,
|
||||
);
|
||||
|
||||
/* Finally update the widget */
|
||||
updateWidget &&
|
||||
updateWidget(
|
||||
updateWidgetParams.operation,
|
||||
|
|
@ -72,7 +182,6 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
|
|||
props.widgetId !== monitor.getItem().widgetId) ||
|
||||
(monitor.isOver() && props.widgetId !== monitor.getItem().widgetId),
|
||||
isExactlyOver: monitor.isOver({ shallow: true }),
|
||||
draggingItem: monitor.getItem() as WidgetProps,
|
||||
}),
|
||||
// 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
|
||||
|
|
@ -87,7 +196,7 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
|
|||
widget,
|
||||
dropTargetOffset,
|
||||
spacesOccupiedBySiblingWidgets,
|
||||
props.snapRows,
|
||||
rows,
|
||||
props.snapColumns,
|
||||
);
|
||||
return !hasCollision;
|
||||
|
|
@ -112,35 +221,55 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
|
|||
}
|
||||
};
|
||||
|
||||
const width =
|
||||
props.widgetId === MAIN_CONTAINER_WIDGET_ID
|
||||
? `calc(100% - ${WIDGET_PADDING * 2}px)`
|
||||
: "100%";
|
||||
|
||||
const height =
|
||||
props.widgetId === MAIN_CONTAINER_WIDGET_ID
|
||||
? `${rows * props.snapRowSpace}px`
|
||||
: "100%";
|
||||
|
||||
const marginTop =
|
||||
props.widgetId === MAIN_CONTAINER_WIDGET_ID ? `${theme.spaces[9]}px` : 0;
|
||||
const marginBottom =
|
||||
props.widgetId === MAIN_CONTAINER_WIDGET_ID ? "500px" : 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={handleFocus}
|
||||
ref={drop}
|
||||
style={{
|
||||
position: "relative",
|
||||
left: 0,
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
top: 0,
|
||||
userSelect: "none",
|
||||
opacity: 0.99,
|
||||
}}
|
||||
<DropTargetContext.Provider
|
||||
value={{ updateDropTargetRows, persistDropTargetRows }}
|
||||
>
|
||||
{props.children}
|
||||
<DragLayerComponent
|
||||
parentOffset={dropTargetOffset}
|
||||
parentRowHeight={props.snapRowSpace}
|
||||
parentColumnWidth={props.snapColumnSpace}
|
||||
visible={isOver || !!isResizing}
|
||||
isOver={isExactlyOver}
|
||||
dropTargetOffset={dropTargetOffset}
|
||||
occupiedSpaces={spacesOccupiedBySiblingWidgets}
|
||||
onBoundsUpdate={handleBoundsUpdate}
|
||||
parentRows={props.snapRows}
|
||||
parentCols={props.snapColumns}
|
||||
isResizing={isResizing}
|
||||
/>
|
||||
</div>
|
||||
<StyledDropTarget
|
||||
onClick={handleFocus}
|
||||
ref={drop}
|
||||
style={{
|
||||
position: "relative",
|
||||
width,
|
||||
height,
|
||||
marginTop,
|
||||
marginBottom,
|
||||
userSelect: "none",
|
||||
opacity: 0.99,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
<DragLayerComponent
|
||||
parentOffset={dropTargetOffset}
|
||||
parentWidgetId={props.widgetId}
|
||||
parentRowHeight={props.snapRowSpace}
|
||||
parentColumnWidth={props.snapColumnSpace}
|
||||
visible={isExactlyOver || isChildResizing}
|
||||
isOver={isExactlyOver}
|
||||
dropTargetOffset={dropTargetOffset}
|
||||
occupiedSpaces={spacesOccupiedBySiblingWidgets}
|
||||
onBoundsUpdate={handleBoundsUpdate}
|
||||
parentRows={rows}
|
||||
parentCols={props.snapColumns}
|
||||
isResizing={isChildResizing}
|
||||
/>
|
||||
</StyledDropTarget>
|
||||
</DropTargetContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +1,30 @@
|
|||
import React, { useLayoutEffect, MutableRefObject } from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import React, { useLayoutEffect, MutableRefObject, memo } from "react";
|
||||
import styled from "styled-components";
|
||||
import { CONTAINER_GRID_PADDING } from "constants/WidgetConstants";
|
||||
type DropTargetMaskProps = {
|
||||
rowHeight: number;
|
||||
columnWidth: number;
|
||||
setBounds?: Function;
|
||||
showGrid?: boolean;
|
||||
};
|
||||
|
||||
export const DropTargetMaskWrapper = styled.div<DropTargetMaskProps>`
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
${props =>
|
||||
props.showGrid &&
|
||||
css`
|
||||
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: -${props => props.columnWidth / 2}px -${props =>
|
||||
props.rowHeight / 2}px;
|
||||
`}
|
||||
left: ${CONTAINER_GRID_PADDING}px;
|
||||
top: ${CONTAINER_GRID_PADDING}px;
|
||||
bottom: ${CONTAINER_GRID_PADDING}px;
|
||||
right: ${CONTAINER_GRID_PADDING}px;
|
||||
|
||||
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: -${props => props.columnWidth / 2}px -${props =>
|
||||
props.rowHeight / 2}px;
|
||||
`;
|
||||
/* 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
|
||||
export const DropTargetMask = memo((props: DropTargetMaskProps) => {
|
||||
const dropTargetMask: MutableRefObject<HTMLDivElement | null> = React.useRef(
|
||||
null,
|
||||
);
|
||||
|
|
@ -44,7 +36,8 @@ export const DropTargetMask = (props: DropTargetMaskProps) => {
|
|||
props.setBounds && props.setBounds(rect);
|
||||
}
|
||||
});
|
||||
|
||||
return <DropTargetMaskWrapper {...props} ref={dropTargetMask} />;
|
||||
};
|
||||
});
|
||||
|
||||
export default DropTargetMask;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
import { GridDefaults } from "constants/WidgetConstants";
|
||||
import { CANVAS_DEFAULT_HEIGHT_PX } from "constants/AppConstants";
|
||||
import { OccupiedSpace } from "constants/editorConstants";
|
||||
|
||||
export const calculateDropTargetRows = (
|
||||
widgetId: string,
|
||||
widgetBottomRow: number,
|
||||
currentDropTargetRows: number,
|
||||
occupiedSpacesByChildren?: OccupiedSpace[],
|
||||
) => {
|
||||
/* Max bottom row including the existing widgets as well as the widget we just dropped */
|
||||
const maxBottomRow =
|
||||
occupiedSpacesByChildren &&
|
||||
Math.max(
|
||||
...occupiedSpacesByChildren
|
||||
.filter(child => child.id !== widgetId)
|
||||
.map(child => child.bottom),
|
||||
widgetBottomRow,
|
||||
);
|
||||
|
||||
let _rows = currentDropTargetRows;
|
||||
/*
|
||||
If the main container's rows are greater than the max bottom row of children widgets (by 4)
|
||||
Update the rows of the container. Making sure that it does not go below the default canvas rows
|
||||
*/
|
||||
if (maxBottomRow && _rows - maxBottomRow > 2) {
|
||||
_rows = Math.max(
|
||||
maxBottomRow + 2,
|
||||
CANVAS_DEFAULT_HEIGHT_PX / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1,
|
||||
);
|
||||
}
|
||||
return _rows + 1;
|
||||
};
|
||||
|
|
@ -2,7 +2,8 @@ import React from "react";
|
|||
import { XYCoord } from "react-dnd";
|
||||
import styled from "styled-components";
|
||||
import { snapToGrid } from "utils/helpers";
|
||||
import { theme } from "constants/DefaultTheme";
|
||||
import { theme, IntentColors } from "constants/DefaultTheme";
|
||||
import { CONTAINER_GRID_PADDING } from "constants/WidgetConstants";
|
||||
|
||||
const DropZoneWrapper = styled.div`
|
||||
position: absolute;
|
||||
|
|
@ -22,6 +23,33 @@ type DropZoneProps = {
|
|||
canDrop: boolean;
|
||||
};
|
||||
|
||||
const generateDropZoneStyles = (
|
||||
props: {
|
||||
visible: boolean;
|
||||
left: number;
|
||||
top: number;
|
||||
height: number;
|
||||
width: number;
|
||||
},
|
||||
canDrop: boolean,
|
||||
isSnapping: boolean,
|
||||
) => {
|
||||
let background = theme.colors.hover;
|
||||
if (!isSnapping) {
|
||||
background = IntentColors.success;
|
||||
} else if (!canDrop) {
|
||||
background = theme.colors.error;
|
||||
}
|
||||
return {
|
||||
display: props.visible ? "block" : "none",
|
||||
left: props.left + "px",
|
||||
width: props.width + "px",
|
||||
top: props.top + "px",
|
||||
height: props.height + "px",
|
||||
background,
|
||||
transition: isSnapping ? "all 0.1s linear" : "none",
|
||||
};
|
||||
};
|
||||
/* eslint-disable react/display-name */
|
||||
export const DropZone = (props: DropZoneProps) => {
|
||||
let wrapperProps = {
|
||||
|
|
@ -31,35 +59,53 @@ export const DropZone = (props: DropZoneProps) => {
|
|||
height: 0,
|
||||
width: 0,
|
||||
};
|
||||
if (props.visible) {
|
||||
if (props.currentOffset && props.currentOffset.x >= props.parentOffset.x) {
|
||||
const [leftColumn, topRow] = snapToGrid(
|
||||
props.parentColumnWidth,
|
||||
props.parentRowHeight,
|
||||
props.currentOffset.x - props.parentOffset.x,
|
||||
props.currentOffset.y - props.parentOffset.y,
|
||||
);
|
||||
wrapperProps = {
|
||||
visible: true,
|
||||
left: leftColumn * props.parentColumnWidth,
|
||||
top: topRow * props.parentRowHeight,
|
||||
height: props.height * props.parentRowHeight,
|
||||
width: props.width * props.parentColumnWidth,
|
||||
};
|
||||
}
|
||||
let wrapperPropsWithSnap = {
|
||||
visible: false,
|
||||
left: 0,
|
||||
top: 0,
|
||||
height: 0,
|
||||
width: 0,
|
||||
};
|
||||
if (
|
||||
props.visible &&
|
||||
props.currentOffset &&
|
||||
props.currentOffset.x >= props.parentOffset.x
|
||||
) {
|
||||
wrapperProps = {
|
||||
visible: true,
|
||||
left: props.currentOffset.x - props.parentOffset.x,
|
||||
top: props.currentOffset.y - props.parentOffset.y,
|
||||
height: props.height * props.parentRowHeight,
|
||||
width: props.width * props.parentColumnWidth,
|
||||
};
|
||||
const [leftColumn, topRow] = snapToGrid(
|
||||
props.parentColumnWidth,
|
||||
props.parentRowHeight,
|
||||
props.currentOffset.x - props.parentOffset.x,
|
||||
props.currentOffset.y - props.parentOffset.y,
|
||||
);
|
||||
wrapperPropsWithSnap = {
|
||||
visible: true,
|
||||
left: leftColumn * props.parentColumnWidth + CONTAINER_GRID_PADDING,
|
||||
top: topRow * props.parentRowHeight + CONTAINER_GRID_PADDING,
|
||||
height: props.height * props.parentRowHeight,
|
||||
width: props.width * props.parentColumnWidth,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<DropZoneWrapper
|
||||
style={{
|
||||
display: wrapperProps.visible ? "block" : "none",
|
||||
left: wrapperProps.left + "px",
|
||||
width: wrapperProps.width + "px",
|
||||
top: wrapperProps.top + "px",
|
||||
height: wrapperProps.height + "px",
|
||||
background: props.canDrop ? theme.colors.hover : theme.colors.error,
|
||||
}}
|
||||
/>
|
||||
<React.Fragment>
|
||||
<DropZoneWrapper
|
||||
style={generateDropZoneStyles(
|
||||
wrapperPropsWithSnap,
|
||||
props.canDrop,
|
||||
true,
|
||||
)}
|
||||
/>
|
||||
<DropZoneWrapper
|
||||
style={generateDropZoneStyles(wrapperProps, props.canDrop, false)}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import React, { useContext, useState, memo } from "react";
|
||||
import { ResizeDirection } from "re-resizable";
|
||||
import { XYCoord } from "react-dnd";
|
||||
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
|
||||
import { getAbsolutePixels } from "utils/helpers";
|
||||
import { WidgetOperations, WidgetRowCols } from "widgets/BaseWidget";
|
||||
import { EditorContext } from "components/editorComponents/EditorContextProvider";
|
||||
import { FocusContext, DragResizeContext } from "pages/Editor/CanvasContexts";
|
||||
import { generateClassName } from "utils/generators";
|
||||
|
||||
import { DropTargetContext } from "./DropTargetComponent";
|
||||
import ResizableContainer, {
|
||||
ResizeBorderDotDiv,
|
||||
ResizableComponentProps,
|
||||
|
|
@ -24,6 +25,9 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
|
|||
// Fetch information from the context
|
||||
const { isDragging, setIsResizing } = useContext(DragResizeContext);
|
||||
const { updateWidget, occupiedSpaces } = useContext(EditorContext);
|
||||
const { updateDropTargetRows, persistDropTargetRows } = useContext(
|
||||
DropTargetContext,
|
||||
);
|
||||
const {
|
||||
showPropertyPane,
|
||||
selectedWidget,
|
||||
|
|
@ -72,14 +76,24 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
|
|||
delta: UIElementSize,
|
||||
position: XYCoord,
|
||||
) => {
|
||||
const isResizePossible = !hasCollision(
|
||||
delta,
|
||||
position,
|
||||
props,
|
||||
occupiedSpacesBySiblingWidgets,
|
||||
);
|
||||
if (isResizePossible === isColliding) {
|
||||
setIsColliding(!isColliding);
|
||||
const bottom =
|
||||
props.bottomRow + (delta.height + position.y) / props.parentRowSpace;
|
||||
|
||||
// Make sure to calculate collision IF we don't update the main container's rows
|
||||
let updated = false;
|
||||
if (updateDropTargetRows && props.parentId === MAIN_CONTAINER_WIDGET_ID)
|
||||
updated = updateDropTargetRows(bottom);
|
||||
|
||||
if (!updated) {
|
||||
const isResizePossible = !hasCollision(
|
||||
delta,
|
||||
position,
|
||||
props,
|
||||
occupiedSpacesBySiblingWidgets,
|
||||
);
|
||||
if (isResizePossible === isColliding) {
|
||||
setIsColliding(!isColliding);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -112,6 +126,9 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
|
|||
);
|
||||
|
||||
if (newRowCols) {
|
||||
persistDropTargetRows &&
|
||||
props.parentId === MAIN_CONTAINER_WIDGET_ID &&
|
||||
persistDropTargetRows(props.widgetId, newRowCols.bottomRow);
|
||||
updateWidget &&
|
||||
updateWidget(WidgetOperations.RESIZE, props.widgetId, newRowCols);
|
||||
}
|
||||
|
|
@ -132,7 +149,7 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
|
|||
const style = getBorderStyles(
|
||||
isWidgetFocused,
|
||||
isColliding,
|
||||
props.paddingOffset,
|
||||
props.paddingOffset - 2,
|
||||
);
|
||||
return (
|
||||
<ResizableContainer
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { theme } from "constants/DefaultTheme";
|
|||
import { WidgetProps, WidgetRowCols } from "widgets/BaseWidget";
|
||||
import { isDropZoneOccupied } from "utils/WidgetPropsUtils";
|
||||
import { OccupiedSpace } from "constants/editorConstants";
|
||||
import { GridDefaults } from "constants/WidgetConstants";
|
||||
|
||||
export type UIElementSize = { height: number; width: number };
|
||||
|
||||
|
|
@ -75,13 +76,22 @@ export const computeUpdatedRowCols = (
|
|||
): WidgetRowCols | false => {
|
||||
if (isColliding) return false;
|
||||
const newRowCols: WidgetRowCols = {
|
||||
leftColumn: props.leftColumn + position.x / props.parentColumnSpace,
|
||||
topRow: props.topRow + position.y / props.parentRowSpace,
|
||||
leftColumn: Math.max(
|
||||
Math.round(props.leftColumn + position.x / props.parentColumnSpace),
|
||||
0,
|
||||
),
|
||||
topRow: Math.round(props.topRow + position.y / props.parentRowSpace),
|
||||
|
||||
rightColumn:
|
||||
props.rightColumn + (delta.width + position.x) / props.parentColumnSpace,
|
||||
bottomRow:
|
||||
rightColumn: Math.min(
|
||||
Math.round(
|
||||
props.rightColumn +
|
||||
(delta.width + position.x) / props.parentColumnSpace,
|
||||
),
|
||||
GridDefaults.DEFAULT_GRID_COLUMNS,
|
||||
),
|
||||
bottomRow: Math.round(
|
||||
props.bottomRow + (delta.height + position.y) / props.parentRowSpace,
|
||||
),
|
||||
};
|
||||
|
||||
if (
|
||||
|
|
|
|||
5
app/client/src/constants/AppConstants.ts
Normal file
5
app/client/src/constants/AppConstants.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export const CANVAS_DEFAULT_WIDTH_PX = 1024;
|
||||
export const CANVAS_DEFAULT_HEIGHT_PX = 1280;
|
||||
export const CANVAS_DEFAULT_GRID_HEIGHT_PX = 1;
|
||||
export const CANVAS_DEFAULT_GRID_WIDTH_PX = 1;
|
||||
export const CANVAS_BACKGROUND_COLOR = "#FFFFFF";
|
||||
|
|
@ -229,7 +229,7 @@ export const theme: Theme = {
|
|||
paneText: Colors.GRAY_CHATEAU,
|
||||
paneSectionLabel: Colors.CADET_BLUE,
|
||||
navBG: Colors.SHARK,
|
||||
grid: Colors.GEYSER,
|
||||
grid: Colors.GEYSER_LIGHT,
|
||||
containerBorder: Colors.FRENCH_PASS,
|
||||
menuButtonBGInactive: Colors.JUNGLE_MIST,
|
||||
menuIconColorInactive: Colors.OXFORD_BLUE,
|
||||
|
|
@ -269,7 +269,7 @@ export const theme: Theme = {
|
|||
sidebarWidth: "300px",
|
||||
headerHeight: "50px",
|
||||
sideNav: {
|
||||
maxWidth: 250,
|
||||
maxWidth: 300,
|
||||
minWidth: 50,
|
||||
bgColor: Colors.OXFORD_BLUE,
|
||||
fontColor: Colors.WHITE,
|
||||
|
|
|
|||
|
|
@ -77,8 +77,14 @@ export const GridDefaults = {
|
|||
DEFAULT_WIDGET_WIDTH: 200,
|
||||
DEFAULT_WIDGET_HEIGHT: 100,
|
||||
DEFAULT_GRID_COLUMNS: 16,
|
||||
DEFAULT_GRID_ROWS: 32,
|
||||
DEFAULT_GRID_ROW_HEIGHT: 40,
|
||||
};
|
||||
|
||||
export const CONTAINER_GRID_PADDING =
|
||||
(GridDefaults.DEFAULT_GRID_ROW_HEIGHT / 2) * 0.6;
|
||||
|
||||
export const WIDGET_PADDING = (GridDefaults.DEFAULT_GRID_ROW_HEIGHT / 2) * 0.4;
|
||||
|
||||
export const WIDGET_CLASSNAME_PREFIX = "WIDGET_";
|
||||
export const MAIN_CONTAINER_WIDGET_ID = "0";
|
||||
export const MAIN_CONTAINER_WIDGET_NAME = "MainContainer";
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import { RenderModes } from "constants/WidgetConstants";
|
|||
import WidgetFactory from "utils/WidgetFactory";
|
||||
import { ContainerWidgetProps } from "widgets/ContainerWidget";
|
||||
|
||||
const PageView = styled.div`
|
||||
flex-grow: 1;
|
||||
const PageView = styled.div<{ width: number }>`
|
||||
height: 100%;
|
||||
margin-top: ${props => props.theme.spaces[1]}px;
|
||||
position: relative;
|
||||
width: ${props => props.width}px;
|
||||
margin: 0 auto;
|
||||
`;
|
||||
|
||||
type AppPageProps = {
|
||||
|
|
@ -18,7 +18,7 @@ type AppPageProps = {
|
|||
|
||||
export const AppPage = (props: AppPageProps) => {
|
||||
return (
|
||||
<PageView>
|
||||
<PageView width={props.dsl.rightColumn}>
|
||||
{props.dsl.widgetId &&
|
||||
WidgetFactory.createWidget(props.dsl, RenderModes.PAGE)}
|
||||
</PageView>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
getIsFetchingPage,
|
||||
getCurrentPageLayoutDSL,
|
||||
} from "selectors/appViewSelectors";
|
||||
import styled from "styled-components";
|
||||
import { ContainerWidgetProps } from "widgets/ContainerWidget";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import { AppViewerRouteParams } from "constants/routes";
|
||||
|
|
@ -15,6 +16,14 @@ import { NonIdealState, Icon, Spinner } from "@blueprintjs/core";
|
|||
import Centered from "components/designSystems/appsmith/CenteredWrapper";
|
||||
import AppPage from "./AppPage";
|
||||
|
||||
const Section = styled.section`
|
||||
background: ${props => props.theme.colors.bodyBG};
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
`;
|
||||
type AppViewerPageContainerProps = {
|
||||
isFetchingPage: boolean;
|
||||
dsl?: ContainerWidgetProps<WidgetProps>;
|
||||
|
|
@ -69,7 +78,11 @@ class AppViewerPageContainer extends Component<AppViewerPageContainerProps> {
|
|||
} else if (!this.props.isFetchingPage && !this.props.dsl) {
|
||||
return pageNotFound;
|
||||
} else if (!this.props.isFetchingPage && this.props.dsl) {
|
||||
return <AppPage dsl={this.props.dsl} />;
|
||||
return (
|
||||
<Section>
|
||||
<AppPage dsl={this.props.dsl} />
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const Canvas = (props: CanvasProps) => {
|
|||
}}
|
||||
>
|
||||
<PropertyPane />
|
||||
<ArtBoard>
|
||||
<ArtBoard width={props.dsl.rightColumn}>
|
||||
{props.dsl.widgetId &&
|
||||
WidgetFactory.createWidget(props.dsl, RenderModes.CANVAS)}
|
||||
</ArtBoard>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import styled from "styled-components";
|
||||
|
||||
export default styled.div`
|
||||
width: 100%;
|
||||
export default styled.div<{ width: number }>`
|
||||
width: ${props => props.width}px;
|
||||
height: 100%;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@ export interface EditorReduxState {
|
|||
currentPageId?: string;
|
||||
currentLayoutId?: string;
|
||||
currentPageName?: string;
|
||||
selectedWidget?: string;
|
||||
focusedWidget?: string;
|
||||
loadingStates: {
|
||||
saving: boolean;
|
||||
savingError: boolean;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { UpdateWidgetPropertyPayload } from "actions/controlActions";
|
|||
import { isDynamicValue } from "utils/DynamicBindingUtils";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import _ from "lodash";
|
||||
import { WidgetTypes } from "constants/WidgetConstants";
|
||||
|
||||
export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
|
||||
try {
|
||||
|
|
@ -111,7 +112,7 @@ export function* moveSaga(moveAction: ReduxAction<WidgetMove>) {
|
|||
// Get parent from DSL/Redux Store
|
||||
const parent = yield select(getWidget, parentId);
|
||||
// Update position of widget
|
||||
widget = updateWidgetPosition(widget, leftColumn, topRow, parent);
|
||||
widget = updateWidgetPosition(widget, leftColumn, topRow);
|
||||
// Replace widget with update widget props
|
||||
widgets[widgetId] = widget;
|
||||
// If the parent has changed i.e parentWidgetId is not parent.widgetId
|
||||
|
|
@ -152,6 +153,10 @@ export function* resizeSaga(resizeAction: ReduxAction<WidgetResize>) {
|
|||
let widget: FlattenedWidgetProps = yield select(getWidget, widgetId);
|
||||
const widgets = yield select(getWidgets);
|
||||
|
||||
if (widget.type === WidgetTypes.CONTAINER_WIDGET) {
|
||||
console.log(resizeAction.payload);
|
||||
widget.snapRows = bottomRow - topRow - 1;
|
||||
}
|
||||
widget = { ...widget, leftColumn, rightColumn, topRow, bottomRow };
|
||||
widgets[widgetId] = widget;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
import { FetchPageResponse } from "api/PageApi";
|
||||
import {
|
||||
CANVAS_DEFAULT_WIDTH_PX,
|
||||
CANVAS_DEFAULT_HEIGHT_PX,
|
||||
CANVAS_BACKGROUND_COLOR,
|
||||
CANVAS_DEFAULT_GRID_HEIGHT_PX,
|
||||
CANVAS_DEFAULT_GRID_WIDTH_PX,
|
||||
} from "constants/AppConstants";
|
||||
import { XYCoord } from "react-dnd";
|
||||
import { ContainerWidgetProps } from "widgets/ContainerWidget";
|
||||
import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer";
|
||||
|
|
@ -9,7 +16,12 @@ import {
|
|||
} from "widgets/BaseWidget";
|
||||
import { WidgetType, RenderModes } from "constants/WidgetConstants";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
import { GridDefaults, WidgetTypes } from "constants/WidgetConstants";
|
||||
import {
|
||||
GridDefaults,
|
||||
WidgetTypes,
|
||||
MAIN_CONTAINER_WIDGET_ID,
|
||||
MAIN_CONTAINER_WIDGET_NAME,
|
||||
} from "constants/WidgetConstants";
|
||||
import { snapToGrid } from "./helpers";
|
||||
import { OccupiedSpace } from "constants/editorConstants";
|
||||
|
||||
|
|
@ -19,7 +31,7 @@ export type WidgetOperationParams = {
|
|||
payload: any;
|
||||
};
|
||||
|
||||
const { DEFAULT_GRID_COLUMNS, DEFAULT_GRID_ROWS } = GridDefaults;
|
||||
const { DEFAULT_GRID_COLUMNS, DEFAULT_GRID_ROW_HEIGHT } = GridDefaults;
|
||||
type Rect = {
|
||||
top: number;
|
||||
left: number;
|
||||
|
|
@ -28,26 +40,38 @@ type Rect = {
|
|||
};
|
||||
|
||||
const defaultDSL = {
|
||||
backgroundColor: "#ffffff",
|
||||
bottomRow: 1024,
|
||||
type: WidgetTypes.CONTAINER_WIDGET,
|
||||
widgetId: MAIN_CONTAINER_WIDGET_ID,
|
||||
widgetName: MAIN_CONTAINER_WIDGET_NAME,
|
||||
|
||||
backgroundColor: CANVAS_BACKGROUND_COLOR,
|
||||
children: [],
|
||||
|
||||
leftColumn: 0,
|
||||
parentColumnSpace: 1,
|
||||
parentRowSpace: 1,
|
||||
renderMode: "CANVAS",
|
||||
rightColumn: 1200,
|
||||
snapColumns: 24,
|
||||
snapRows: 32,
|
||||
rightColumn: CANVAS_DEFAULT_WIDTH_PX,
|
||||
parentColumnSpace: CANVAS_DEFAULT_GRID_WIDTH_PX,
|
||||
snapColumns: GridDefaults.DEFAULT_GRID_COLUMNS,
|
||||
|
||||
topRow: 0,
|
||||
type: "CONTAINER_WIDGET",
|
||||
widgetId: "0",
|
||||
widgetName: "MainContainer",
|
||||
bottomRow: CANVAS_DEFAULT_HEIGHT_PX,
|
||||
parentRowSpace: CANVAS_DEFAULT_GRID_HEIGHT_PX,
|
||||
// 1 row needs to be removed, as padding top and bottom takes up some 1 row worth of space.
|
||||
// Widget padding: 8px
|
||||
// Container padding: 12px;
|
||||
// Total = (8 + 12) * 2 = GridDefaults.DEFAULT_GRID_ROW_HEIGHT = 40
|
||||
snapRows: CANVAS_DEFAULT_HEIGHT_PX / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1,
|
||||
};
|
||||
|
||||
export const extractCurrentDSL = (
|
||||
fetchPageResponse: FetchPageResponse,
|
||||
): ContainerWidgetProps<WidgetProps> => {
|
||||
const currentDSL = fetchPageResponse.data.layouts[0].dsl || defaultDSL;
|
||||
// 1 row needs to be removed, as padding top and bottom takes up some 1 row worth of space.
|
||||
// Widget padding: 8px
|
||||
// Container padding: 12px;
|
||||
// Total = (8 + 12) * 2 = GridDefaults.DEFAULT_GRID_ROW_HEIGHT = 40
|
||||
currentDSL.snapRows =
|
||||
Math.floor(currentDSL.bottomRow / DEFAULT_GRID_ROW_HEIGHT) - 1;
|
||||
return currentDSL;
|
||||
};
|
||||
|
||||
|
|
@ -99,11 +123,14 @@ export const isDropZoneOccupied = (
|
|||
export const isWidgetOverflowingParentBounds = (
|
||||
parentRowCols: { rows?: number; cols?: number },
|
||||
offset: Rect,
|
||||
) => {
|
||||
return (
|
||||
): boolean => {
|
||||
const result =
|
||||
offset.right < 0 ||
|
||||
offset.top < 0 ||
|
||||
(parentRowCols.cols || GridDefaults.DEFAULT_GRID_COLUMNS) < offset.right ||
|
||||
(parentRowCols.rows || GridDefaults.DEFAULT_GRID_ROWS) < offset.bottom
|
||||
);
|
||||
(parentRowCols.rows || 0) < offset.bottom;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const noCollision = (
|
||||
|
|
@ -123,6 +150,9 @@ export const noCollision = (
|
|||
clientOffset as XYCoord,
|
||||
dropTargetOffset,
|
||||
);
|
||||
if (left < 0 || top < 0) {
|
||||
return false;
|
||||
}
|
||||
const widgetWidth = widget.columns
|
||||
? widget.columns
|
||||
: widget.rightColumn - widget.leftColumn;
|
||||
|
|
@ -143,6 +173,23 @@ export const noCollision = (
|
|||
return false;
|
||||
};
|
||||
|
||||
export const currentDropRow = (
|
||||
dropTargetRowSpace: number,
|
||||
dropTargetVerticalOffset: number,
|
||||
draggableItemVerticalOffset: number,
|
||||
widget: WidgetProps & Partial<WidgetConfigProps>,
|
||||
) => {
|
||||
const widgetHeight = widget.rows
|
||||
? widget.rows
|
||||
: widget.bottomRow - widget.topRow;
|
||||
const top = Math.round(
|
||||
(draggableItemVerticalOffset - dropTargetVerticalOffset) /
|
||||
dropTargetRowSpace,
|
||||
);
|
||||
const currentBottomOffset = top + widgetHeight;
|
||||
return currentBottomOffset;
|
||||
};
|
||||
|
||||
export const widgetOperationParams = (
|
||||
widget: WidgetProps & Partial<WidgetConfigProps>,
|
||||
widgetOffset: XYCoord,
|
||||
|
|
@ -196,7 +243,6 @@ export const updateWidgetPosition = (
|
|||
widget: WidgetProps,
|
||||
leftColumn: number,
|
||||
topRow: number,
|
||||
parent?: WidgetProps,
|
||||
) => {
|
||||
const newPositions = {
|
||||
leftColumn,
|
||||
|
|
@ -204,31 +250,16 @@ export const updateWidgetPosition = (
|
|||
rightColumn: leftColumn + (widget.rightColumn - widget.leftColumn),
|
||||
bottomRow: topRow + (widget.bottomRow - widget.topRow),
|
||||
};
|
||||
if (parent) {
|
||||
if (widget.type === WidgetTypes.CONTAINER_WIDGET) {
|
||||
widget.snapRows = newPositions.bottomRow - newPositions.topRow - 1;
|
||||
}
|
||||
|
||||
return {
|
||||
...widget,
|
||||
...newPositions,
|
||||
};
|
||||
};
|
||||
|
||||
export const updateWidgetSize = (
|
||||
widget: WidgetProps,
|
||||
deltaHeight: number,
|
||||
deltaWidth: number,
|
||||
): WidgetProps => {
|
||||
const origHeight = (widget.bottomRow - widget.topRow) * widget.parentRowSpace;
|
||||
const origWidth =
|
||||
(widget.rightColumn - widget.leftColumn) * widget.parentColumnSpace;
|
||||
return {
|
||||
...widget,
|
||||
rightColumn:
|
||||
widget.leftColumn + (origWidth + deltaWidth) / widget.parentColumnSpace,
|
||||
bottomRow:
|
||||
widget.topRow + (origHeight + deltaHeight) / widget.parentRowSpace,
|
||||
};
|
||||
};
|
||||
|
||||
export const generateWidgetProps = (
|
||||
parent: ContainerWidgetProps<WidgetProps>,
|
||||
type: WidgetType,
|
||||
|
|
@ -252,7 +283,7 @@ export const generateWidgetProps = (
|
|||
if (type === WidgetTypes.CONTAINER_WIDGET) {
|
||||
others = {
|
||||
snapColumns: DEFAULT_GRID_COLUMNS,
|
||||
snapRows: DEFAULT_GRID_ROWS,
|
||||
snapRows: rows - 1,
|
||||
orientation: "VERTICAL",
|
||||
children: [],
|
||||
};
|
||||
|
|
@ -270,9 +301,9 @@ export const generateWidgetProps = (
|
|||
...others,
|
||||
};
|
||||
} else {
|
||||
if (parent)
|
||||
if (parent) {
|
||||
throw Error("Failed to create widget: Parent's size cannot be calculate");
|
||||
else throw Error("Failed to create widget: Parent was not provided ");
|
||||
} else throw Error("Failed to create widget: Parent was not provided ");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ export const snapToGrid = (
|
|||
x: number,
|
||||
y: number,
|
||||
) => {
|
||||
const snappedX = Math.floor(x / columnWidth);
|
||||
const snappedY = Math.floor(y / rowHeight);
|
||||
const snappedX = Math.round(x / columnWidth);
|
||||
const snappedY = Math.round(y / rowHeight);
|
||||
return [snappedX, snappedY];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import moment from "moment";
|
|||
|
||||
const STORAGE_KEYS: { [id: string]: string } = {
|
||||
AUTH_EXPIRATION: "Auth.expiration",
|
||||
ROUTE_BEFORE_LOGIN: "RedirectPath",
|
||||
};
|
||||
|
||||
const store = localforage.createInstance({
|
||||
|
|
@ -25,3 +26,20 @@ export const hasAuthExpired = async () => {
|
|||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const setRouteBeforeLogin = (path: string | null) => {
|
||||
store.setItem(STORAGE_KEYS.ROUTE_BEFORE_LOGIN, path).catch(error => {
|
||||
console.log("Unable to set last path");
|
||||
});
|
||||
};
|
||||
|
||||
export const getRouteBeforeLogin = async () => {
|
||||
const routeBeforeLogin: string = await store.getItem(
|
||||
STORAGE_KEYS.ROUTE_BEFORE_LOGIN,
|
||||
);
|
||||
if (routeBeforeLogin && routeBeforeLogin.length > 0) {
|
||||
setRouteBeforeLogin(null);
|
||||
return routeBeforeLogin;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,12 @@ import {
|
|||
CSSUnits,
|
||||
} from "constants/WidgetConstants";
|
||||
import React, { Component } from "react";
|
||||
import { PositionType, CSSUnit } from "constants/WidgetConstants";
|
||||
import {
|
||||
PositionType,
|
||||
CSSUnit,
|
||||
CONTAINER_GRID_PADDING,
|
||||
WidgetTypes,
|
||||
} from "constants/WidgetConstants";
|
||||
import _ from "lodash";
|
||||
import DraggableComponent from "components/editorComponents/DraggableComponent";
|
||||
import ResizableComponent from "components/editorComponents/ResizableComponent";
|
||||
|
|
@ -126,6 +131,7 @@ abstract class BaseWidget<
|
|||
componentWidth: (rightColumn - leftColumn) * parentColumnSpace,
|
||||
componentHeight: (bottomRow - topRow) * parentRowSpace,
|
||||
};
|
||||
|
||||
if (
|
||||
_.isNil(this.state) ||
|
||||
widgetState.componentHeight !== this.state.componentHeight ||
|
||||
|
|
@ -161,15 +167,17 @@ abstract class BaseWidget<
|
|||
</PositionedContainer>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<PositionedContainer style={style}>
|
||||
{this.getCanvasView()}
|
||||
</PositionedContainer>
|
||||
);
|
||||
return this.getCanvasView();
|
||||
case RenderModes.PAGE:
|
||||
if (this.props.isVisible) {
|
||||
return (
|
||||
<PositionedContainer style={this.getPositionStyle()}>
|
||||
<PositionedContainer
|
||||
style={this.getPositionStyle()}
|
||||
isMainContainer={
|
||||
this.props.type === WidgetTypes.CONTAINER_WIDGET &&
|
||||
this.props.widgetId === "0"
|
||||
}
|
||||
>
|
||||
{this.getPageView()}
|
||||
</PositionedContainer>
|
||||
);
|
||||
|
|
@ -200,8 +208,11 @@ abstract class BaseWidget<
|
|||
positionType: PositionTypes.ABSOLUTE,
|
||||
componentHeight: this.state.componentHeight,
|
||||
componentWidth: this.state.componentWidth,
|
||||
yPosition: this.props.topRow * this.props.parentRowSpace,
|
||||
xPosition: this.props.leftColumn * this.props.parentColumnSpace,
|
||||
yPosition:
|
||||
this.props.topRow * this.props.parentRowSpace + CONTAINER_GRID_PADDING,
|
||||
xPosition:
|
||||
this.props.leftColumn * this.props.parentColumnSpace +
|
||||
CONTAINER_GRID_PADDING,
|
||||
xPositionUnit: CSSUnits.PIXEL,
|
||||
yPositionUnit: CSSUnits.PIXEL,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,13 +8,16 @@ import { ContainerOrientation, WidgetType } from "constants/WidgetConstants";
|
|||
import WidgetFactory from "utils/WidgetFactory";
|
||||
import { Color } from "constants/Colors";
|
||||
import DropTargetComponent from "components/editorComponents/DropTargetComponent";
|
||||
import { GridDefaults } from "constants/WidgetConstants";
|
||||
import {
|
||||
GridDefaults,
|
||||
CONTAINER_GRID_PADDING,
|
||||
WIDGET_PADDING,
|
||||
} from "constants/WidgetConstants";
|
||||
|
||||
import ResizeBoundsContainerComponent from "components/editorComponents/ResizeBoundsContainerComponent";
|
||||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||
|
||||
const { DEFAULT_GRID_COLUMNS, DEFAULT_GRID_ROW_HEIGHT } = GridDefaults;
|
||||
|
||||
class ContainerWidget extends BaseWidget<
|
||||
ContainerWidgetProps<WidgetProps>,
|
||||
ContainerWidgetState
|
||||
|
|
@ -34,10 +37,10 @@ class ContainerWidget extends BaseWidget<
|
|||
super.componentDidUpdate(previousProps);
|
||||
let snapColumnSpace = this.state.snapColumnSpace;
|
||||
if (this.state.componentWidth)
|
||||
snapColumnSpace = Math.floor(
|
||||
this.state.componentWidth /
|
||||
(this.props.snapColumns || DEFAULT_GRID_COLUMNS),
|
||||
);
|
||||
snapColumnSpace =
|
||||
(this.state.componentWidth -
|
||||
(CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2) /
|
||||
(this.props.snapColumns || DEFAULT_GRID_COLUMNS);
|
||||
if (this.state.snapColumnSpace !== snapColumnSpace) {
|
||||
this.setState({
|
||||
snapColumnSpace,
|
||||
|
|
@ -70,9 +73,7 @@ class ContainerWidget extends BaseWidget<
|
|||
getContainerComponentProps = () => {
|
||||
const containerProps: ContainerWidgetProps<WidgetProps> = { ...this.props };
|
||||
containerProps.backgroundColor = this.props.backgroundColor || "white";
|
||||
if (!this.props.parentId) {
|
||||
containerProps.containerStyle = "none";
|
||||
}
|
||||
|
||||
return containerProps;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user