PromucFlow_constructor/app/client/src/components/editorComponents/ResizableComponent.tsx

269 lines
7.4 KiB
TypeScript
Raw Normal View History

2019-10-08 06:19:10 +00:00
import React, { useContext, CSSProperties, useState } from "react";
2019-10-30 10:23:20 +00:00
import styled, { css } from "styled-components";
import { Rnd } from "react-rnd";
import { XYCoord } from "react-dnd";
2019-10-30 10:23:20 +00:00
import { WidgetProps, WidgetOperations } from "../../widgets/BaseWidget";
import { OccupiedSpaceContext } from "../../widgets/ContainerWidget";
import {
ContainerProps,
ParentBoundsContext,
2019-11-05 05:09:50 +00:00
} from "../designSystems/appsmith/ContainerComponent";
2019-10-30 10:23:20 +00:00
import { isDropZoneOccupied } from "../../utils/WidgetPropsUtils";
import { FocusContext } from "../../pages/Editor/Canvas";
import { DraggableComponentContext } from "./DraggableComponent";
2019-10-30 10:23:20 +00:00
import { WidgetFunctionsContext } from "../../pages/Editor/WidgetsEditor";
2019-10-08 12:31:58 +00:00
import { ResizingContext } from "./DropTargetComponent";
import {
theme,
getColorWithOpacity,
getBorderCSSShorthand,
2019-10-30 10:23:20 +00:00
} from "../../constants/DefaultTheme";
export type ResizableComponentProps = WidgetProps & ContainerProps;
2019-10-30 10:23:20 +00:00
const BORDER_INDEX = 1;
2019-10-30 10:23:20 +00:00
const HOVER_AREA_WIDTH = 12;
2019-10-30 10:23:20 +00:00
function getHandleSyles(): {
top: CSSProperties;
bottom: CSSProperties;
right: CSSProperties;
left: CSSProperties;
bottomRight: CSSProperties;
bottomLeft: CSSProperties;
} {
2019-10-30 10:23:20 +00:00
const hoverWidth = HOVER_AREA_WIDTH;
const hoverWidthHalf = hoverWidth / 2;
const halfBorder = theme.borders[BORDER_INDEX].thickness / 2;
const shiftedHoverWidthHalf = hoverWidthHalf + halfBorder;
const hoverCornerWidth = hoverWidth + hoverWidth / 4;
return {
top: {
height: hoverWidth + "px",
top: "-" + shiftedHoverWidthHalf + "px",
zIndex: 1,
cursor: "ns-resize",
},
bottomRight: {
height: hoverCornerWidth + "px",
width: hoverCornerWidth + "px",
zIndex: 1,
cursor: "nwse-resize",
},
bottomLeft: {
height: hoverCornerWidth + "px",
width: hoverCornerWidth + "px",
zIndex: 1,
cursor: "nesw-resize",
},
bottom: {
height: hoverWidth + "px",
bottom: "-" + shiftedHoverWidthHalf + "px",
zIndex: 1,
cursor: "ns-resize",
},
left: {
width: hoverWidth + "px",
left: "-" + shiftedHoverWidthHalf + "px",
zIndex: 1,
cursor: "ew-resize",
},
right: {
width: hoverWidth + "px",
right: "-" + shiftedHoverWidthHalf + "px",
zIndex: 1,
cursor: "ew-resize",
},
2019-10-30 10:23:20 +00:00
};
}
2019-10-29 10:03:04 +00:00
interface ResizeBorderDotDivProps {
2019-10-30 10:23:20 +00:00
isfocused: boolean;
2019-10-29 10:03:04 +00:00
}
const borderCSS = css<ResizeBorderDotDivProps>`
position: relative;
2019-10-29 10:03:04 +00:00
height: 100%;
opacity: 0.99;
&:after,
&:before {
content: "";
position: absolute;
width: ${props => props.theme.spaces[2]}px;
height: ${props => props.theme.spaces[2]}px;
border-radius: ${props => props.theme.radii[5]}%;
background: ${props =>
props.isfocused && props.theme.colors.containerBorder};
}
2019-10-29 10:03:04 +00:00
`;
const ResizeBorderDotDiv = styled.div<ResizeBorderDotDivProps>`
${borderCSS}
&:after {
left: -${props => props.theme.spaces[1]}px;
top: calc(50% - ${props => props.theme.spaces[1]}px);
z-index: 0;
}
&:before {
left: calc(50% - ${props => props.theme.spaces[1]}px);
top: -${props => props.theme.spaces[1]}px;
z-index: 1;
}
`;
const ResizableContainer = styled(Rnd)`
${borderCSS}
&:after {
right: -${props => props.theme.spaces[1]}px;
top: calc(50% - ${props => props.theme.spaces[1]}px);
z-index: 0;
}
&:before {
left: calc(50% - ${props => props.theme.spaces[1]}px);
bottom: -${props => props.theme.spaces[1]}px;
z-index: 1;
}
`;
export const ResizableComponent = (props: ResizableComponentProps) => {
const { isDragging, widgetNode } = useContext(DraggableComponentContext);
2019-10-08 12:31:58 +00:00
const { setIsResizing } = useContext(ResizingContext);
const { boundingParent } = useContext(ParentBoundsContext);
const { updateWidget } = useContext(WidgetFunctionsContext);
const { showPropertyPane, isFocused, setFocus } = useContext(FocusContext);
2019-10-08 06:19:10 +00:00
const occupiedSpaces = useContext(OccupiedSpaceContext);
2019-10-08 12:31:58 +00:00
2019-10-08 06:19:10 +00:00
const [isColliding, setIsColliding] = useState(false);
2019-10-30 10:23:20 +00:00
const isWidgetFocused = isFocused === props.widgetId;
2019-10-08 06:19:10 +00:00
let bounds = "body";
if (boundingParent && boundingParent.current) {
bounds = "." + boundingParent.current.className.split(" ")[1];
}
2019-10-08 06:19:10 +00:00
const checkForCollision = (
e: Event,
dir: any,
ref: any,
delta: { width: number; height: number },
position: XYCoord,
) => {
const left = props.leftColumn + position.x / props.parentColumnSpace;
const top = props.topRow + position.y / props.parentRowSpace;
const right =
props.rightColumn + (delta.width + position.x) / props.parentColumnSpace;
const bottom =
props.bottomRow + (delta.height + position.y) / props.parentRowSpace;
if (
isDropZoneOccupied(
{
left,
top,
bottom,
right,
},
props.widgetId,
occupiedSpaces,
)
) {
setIsColliding(true);
} else {
if (!!isColliding) {
setIsColliding(false);
}
}
};
const updateSize = (
e: Event,
dir: any,
ref: any,
delta: { width: number; height: number },
position: XYCoord,
) => {
2019-10-03 15:38:46 +00:00
setIsResizing && setIsResizing(false);
2019-10-08 06:19:10 +00:00
setFocus && setFocus(props.widgetId);
showPropertyPane && showPropertyPane(props.widgetId, widgetNode);
2019-10-08 12:31:58 +00:00
const leftColumn = props.leftColumn + position.x / props.parentColumnSpace;
const topRow = props.topRow + position.y / props.parentRowSpace;
const rightColumn =
props.rightColumn + (delta.width + position.x) / props.parentColumnSpace;
const bottomRow =
props.bottomRow + (delta.height + position.y) / props.parentRowSpace;
2019-10-09 09:08:55 +00:00
if (
!isColliding &&
(props.leftColumn !== leftColumn ||
props.topRow !== topRow ||
props.bottomRow !== bottomRow ||
props.rightColumn !== rightColumn)
) {
2019-10-08 06:19:10 +00:00
updateWidget &&
updateWidget(WidgetOperations.RESIZE, props.widgetId, {
leftColumn,
rightColumn,
topRow,
bottomRow,
});
}
setIsColliding(false);
};
2019-10-08 12:31:58 +00:00
2019-10-29 10:03:04 +00:00
const canResize = !isDragging && isWidgetFocused;
return (
<ResizableContainer
2019-10-29 10:03:04 +00:00
isfocused={isWidgetFocused ? "true" : undefined}
position={{
x: 0,
y: 0,
}}
size={{
2019-09-24 12:36:03 +00:00
width: props.style.componentWidth as number,
height: props.style.componentHeight as number,
}}
disableDragging
minWidth={props.parentColumnSpace}
minHeight={props.parentRowSpace}
2019-10-08 06:19:10 +00:00
style={{
...props.style,
border:
isFocused === props.widgetId
? getBorderCSSShorthand(theme.borders[BORDER_INDEX])
: "none",
borderColor: isColliding
? getColorWithOpacity(theme.colors.error, 0.6)
: "inherit",
2019-10-08 06:19:10 +00:00
}}
onResizeStop={updateSize}
2019-10-08 06:19:10 +00:00
onResize={checkForCollision}
2019-10-03 15:38:46 +00:00
onResizeStart={() => {
setIsResizing && setIsResizing(true);
showPropertyPane && showPropertyPane(props.widgetId);
2019-10-03 15:38:46 +00:00
}}
resizeGrid={[props.parentColumnSpace, props.parentRowSpace]}
bounds={bounds}
resizeHandleStyles={getHandleSyles()}
enableResizing={{
2019-10-08 12:31:58 +00:00
top: canResize,
right: canResize,
bottom: canResize,
left: canResize,
topRight: canResize,
topLeft: canResize,
bottomRight: canResize,
bottomLeft: canResize,
}}
>
2019-10-29 10:03:04 +00:00
<ResizeBorderDotDiv isfocused={isWidgetFocused}>
{props.children}
</ResizeBorderDotDiv>
</ResizableContainer>
);
};
export default ResizableComponent;