Fix cherry pick conflicts

This commit is contained in:
Abhinav 2020-02-19 01:26:58 +05:30
parent ed9cc21d3d
commit 2340250ba0
9 changed files with 471 additions and 323 deletions

View File

@ -68,7 +68,6 @@
"react-helmet": "^5.2.1", "react-helmet": "^5.2.1",
"react-monaco-editor": "^0.31.1", "react-monaco-editor": "^0.31.1",
"react-redux": "^7.1.3", "react-redux": "^7.1.3",
"react-rnd": "^10.1.1",
"react-router": "^5.1.2", "react-router": "^5.1.2",
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
"react-scripts": "^3.3.0", "react-scripts": "^3.3.0",
@ -77,6 +76,7 @@
"react-tabs": "^3.0.0", "react-tabs": "^3.0.0",
"react-toastify": "^5.5.0", "react-toastify": "^5.5.0",
"react-transition-group": "^4.3.0", "react-transition-group": "^4.3.0",
"react-use-gesture": "^7.0.4",
"redux": "^4.0.1", "redux": "^4.0.1",
"redux-form": "^8.2.6", "redux-form": "^8.2.6",
"redux-saga": "^1.1.3", "redux-saga": "^1.1.3",

View File

@ -218,6 +218,7 @@ const DraggableComponent = (props: DraggableComponentProps) => {
userSelect: "none", userSelect: "none",
cursor: "drag", cursor: "drag",
zIndex: stackingContext, zIndex: stackingContext,
pointerEvents: !isResizingOrDragging ? "auto" : "none",
}} }}
> >
{selectedWidget !== props.widgetId && props.isDefaultClickDisabled && ( {selectedWidget !== props.widgetId && props.isDefaultClickDisabled && (

View File

@ -73,6 +73,9 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
const isResizing = useSelector( const isResizing = useSelector(
(state: AppState) => state.ui.widgetDragResize.isResizing, (state: AppState) => state.ui.widgetDragResize.isResizing,
); );
const isDragging = useSelector(
(state: AppState) => state.ui.widgetDragResize.isDragging,
);
const spacesOccupiedBySiblingWidgets = const spacesOccupiedBySiblingWidgets =
occupiedSpaces && occupiedSpaces[props.widgetId] occupiedSpaces && occupiedSpaces[props.widgetId]
@ -224,7 +227,7 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
}; };
const handleFocus = () => { const handleFocus = () => {
if (!props.parentId && !isResizing) { if (!props.parentId && !isResizing && !isDragging) {
selectWidget && selectWidget(props.widgetId); selectWidget && selectWidget(props.widgetId);
showPropertyPane && showPropertyPane(); showPropertyPane && showPropertyPane();
} }

View File

@ -1,25 +1,24 @@
import React, { useContext, useState, memo } from "react"; import React, { useContext, memo } from "react";
import { ResizeDirection } from "re-resizable"; import { css } from "styled-components";
import { XYCoord } from "react-dnd"; import { XYCoord } from "react-dnd";
import { import {
MAIN_CONTAINER_WIDGET_ID, MAIN_CONTAINER_WIDGET_ID,
WidgetTypes, WidgetTypes,
} from "constants/WidgetConstants"; } from "constants/WidgetConstants";
import { getAbsolutePixels } from "utils/helpers"; import { ContainerWidgetProps } from "widgets/ContainerWidget";
import { WidgetOperations, WidgetRowCols } from "widgets/BaseWidget";
import {
WidgetOperations,
WidgetRowCols,
WidgetProps,
} from "widgets/BaseWidget";
import { EditorContext } from "components/editorComponents/EditorContextProvider"; import { EditorContext } from "components/editorComponents/EditorContextProvider";
import { generateClassName } from "utils/generators"; import { generateClassName } from "utils/generators";
import { DropTargetContext } from "./DropTargetComponent"; import { DropTargetContext } from "./DropTargetComponent";
import ResizableContainer, {
ResizeBorderDotDiv,
ResizableComponentProps,
} from "./ResizableContainer";
import { import {
UIElementSize, UIElementSize,
getHandleSyles, computeFinalRowCols,
computeUpdatedRowCols, computeRowCols,
hasCollision,
getBorderStyles,
} from "./ResizableUtils"; } from "./ResizableUtils";
import { import {
useShowPropertyPane, useShowPropertyPane,
@ -28,6 +27,96 @@ import {
} from "utils/hooks/dragResizeHooks"; } from "utils/hooks/dragResizeHooks";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { AppState } from "reducers"; import { AppState } from "reducers";
import { PropertyPaneReduxState } from "reducers/uiReducers/propertyPaneReducer";
import Resizable from "resizable";
import { theme } from "constants/DefaultTheme";
import { isDropZoneOccupied } from "utils/WidgetPropsUtils";
export type ResizableComponentProps = ContainerWidgetProps<WidgetProps> & {
paddingOffset: number;
};
const HandleStyles = css`
position: absolute;
z-index: 3;
width: 20px;
height: 20px;
&:before {
position: absolute;
background: ${theme.colors.widgetBorder};
content: "";
width: 2px;
height: 2px;
}
&:after {
position: absolute;
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
background: ${theme.colors.widgetBorder};
top: calc(50% - 2px);
left: calc(50% - 2px);
}
`;
const VerticalHandleStyles = css`
${HandleStyles}
top:0;
height: 100%;
cursor: col-resize;
&:before {
left: 50%;
height: 100%;
top: 0;
}
&:after {
}
`;
const HorizontalHandleStyles = css`
${HandleStyles}
left: 0;
width: 100%;
cursor: row-resize;
&:before {
top: 50%;
width: 100%;
left: 0;
}
&:after {
}
`;
const LeftHandleStyles = css`
${VerticalHandleStyles}
left:-10px;
`;
const RightHandleStyles = css`
${VerticalHandleStyles};
right: -10px;
`;
const TopHandleStyles = css`
${HorizontalHandleStyles};
top: -10px;
`;
const BottomHandleStyles = css`
${HorizontalHandleStyles};
bottom: -10px;
`;
const BottomRightHandleStyles = css`
position: absolute;
z-index: 3;
bottom: -20px;
right: -20px;
width: 40px;
height: 40px;
cursor: se-resize;
`;
/* eslint-disable react/display-name */ /* eslint-disable react/display-name */
export const ResizableComponent = memo((props: ResizableComponentProps) => { export const ResizableComponent = memo((props: ResizableComponentProps) => {
@ -50,6 +139,13 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
const isDragging = useSelector( const isDragging = useSelector(
(state: AppState) => state.ui.widgetDragResize.isDragging, (state: AppState) => state.ui.widgetDragResize.isDragging,
); );
const isResizing = useSelector(
(state: AppState) => state.ui.widgetDragResize.isResizing,
);
const propertyPaneState: PropertyPaneReduxState = useSelector(
(state: AppState) => state.ui.propertyPane,
);
const occupiedSpacesBySiblingWidgets = const occupiedSpacesBySiblingWidgets =
occupiedSpaces && props.parentId && occupiedSpaces[props.parentId] occupiedSpaces && props.parentId && occupiedSpaces[props.parentId]
@ -69,18 +165,10 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
); );
} }
// Use state flag - isColliding - use to figure out if resize is possible at the current size.
const [isColliding, setIsColliding] = useState(false);
// isFocused (string | boolean) -> isWidgetFocused (boolean) // isFocused (string | boolean) -> isWidgetFocused (boolean)
const isWidgetFocused = const isWidgetFocused =
focusedWidget === props.widgetId || selectedWidget === props.widgetId; focusedWidget === props.widgetId || selectedWidget === props.widgetId;
// Widget can be resized if
// The widget is focused, and
// There is no drag event in progress on a widget.
const canResize = !isDragging && isWidgetFocused;
// Calculate the dimensions of the widget, // Calculate the dimensions of the widget,
// The ResizableContainer's size prop is controlled // The ResizableContainer's size prop is controlled
const dimensions: UIElementSize = { const dimensions: UIElementSize = {
@ -93,40 +181,90 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
// whose clientRect will act as the bounds for resizing. // whose clientRect will act as the bounds for resizing.
// Note, if there are many containers with the same className // Note, if there are many containers with the same className
// the bounding container becomes the nearest parent with the className // the bounding container becomes the nearest parent with the className
let bounds = "body"; const boundingElementClassName = generateClassName(props.parentId);
bounds = "." + generateClassName(props.parentId); const possibleBoundingElements = document.getElementsByClassName(
boundingElementClassName,
);
const boundingElement =
possibleBoundingElements.length > 0
? possibleBoundingElements[0]
: undefined;
const boundingElementClientRect = boundingElement
? boundingElement.getBoundingClientRect()
: undefined;
// onResize handler // onResize handler
// Checks if the current resize position has any collisions // Checks if the current resize position has any collisions
// If yes, set isColliding flag to true. // If yes, set isColliding flag to true.
// If no, set isColliding flag to false. // If no, set isColliding flag to false.
const checkForCollision = ( const isColliding = (newDimensions: UIElementSize, position: XYCoord) => {
e: MouseEvent,
dir: ResizeDirection,
ref: HTMLDivElement,
delta: UIElementSize,
position: XYCoord,
) => {
const bottom = const bottom =
props.bottomRow + (delta.height + position.y) / props.parentRowSpace; props.topRow +
position.y / props.parentRowSpace +
newDimensions.height / props.parentRowSpace;
// Make sure to calculate collision IF we don't update the main container's rows // Make sure to calculate collision IF we don't update the main container's rows
let updated = false; let updated = false;
if (updateDropTargetRows && props.parentId === MAIN_CONTAINER_WIDGET_ID) if (updateDropTargetRows && props.parentId === MAIN_CONTAINER_WIDGET_ID) {
updated = updateDropTargetRows(bottom); updated = updateDropTargetRows(bottom);
}
const delta: UIElementSize = {
height: newDimensions.height - dimensions.height,
width: newDimensions.width - dimensions.width,
};
const newRowCols: WidgetRowCols | false = computeRowCols(
delta,
position,
props,
);
if (
boundingElementClientRect &&
newRowCols.rightColumn * props.parentColumnSpace >
boundingElementClientRect.width
) {
return true;
}
if (newRowCols && newRowCols.leftColumn < 0) {
return true;
}
if (!updated) { if (!updated) {
const isResizePossible = !hasCollision( if (
delta, // If this is a container widget, the maxBottomRow of child widgets should be one less than the max bottom row of the new row cols
position, maxBottomRowOfChildWidgets &&
props, newRowCols &&
occupiedSpacesBySiblingWidgets, props.type === WidgetTypes.CONTAINER_WIDGET &&
maxBottomRowOfChildWidgets, newRowCols.bottomRow - newRowCols.topRow - 1 <
); maxBottomRowOfChildWidgets
if (isResizePossible === isColliding) { ) {
setIsColliding(!isColliding); return true;
}
if (
boundingElementClientRect &&
newRowCols.bottomRow * props.parentRowSpace >
boundingElementClientRect.height
) {
return true;
}
if (newRowCols && newRowCols.topRow < 0) {
return true;
} }
} }
// Check if new row cols are occupied by sibling widgets
return isDropZoneOccupied(
{
left: newRowCols.leftColumn,
top: newRowCols.topRow,
bottom: newRowCols.bottomRow,
right: newRowCols.rightColumn,
},
props.widgetId,
occupiedSpacesBySiblingWidgets,
);
}; };
// onResizeStop handler // onResizeStop handler
@ -134,24 +272,17 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
// 1) There is no collision // 1) There is no collision
// 2) There is a change in widget size // 2) There is a change in widget size
// Update widget, if both of the above are true. // Update widget, if both of the above are true.
const updateSize = ( const updateSize = (newDimensions: UIElementSize, position: XYCoord) => {
e: MouseEvent,
dir: ResizeDirection,
ref: HTMLDivElement,
d: UIElementSize,
position: XYCoord,
) => {
// Get the difference in size of the widget, before and after resizing. // Get the difference in size of the widget, before and after resizing.
const delta: UIElementSize = { const delta: UIElementSize = {
height: getAbsolutePixels(ref.style.height) - dimensions.height, height: newDimensions.height - dimensions.height,
width: getAbsolutePixels(ref.style.width) - dimensions.width, width: newDimensions.width - dimensions.width,
}; };
// Get the updated Widget rows and columns props // Get the updated Widget rows and columns props
// False, if there is collision // False, if there is collision
// False, if none of the rows and cols have changed. // False, if none of the rows and cols have changed.
const newRowCols: WidgetRowCols | false = computeUpdatedRowCols( const newRowCols: WidgetRowCols | false = computeFinalRowCols(
isColliding,
delta, delta,
position, position,
props, props,
@ -165,7 +296,7 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
updateWidget(WidgetOperations.RESIZE, props.widgetId, newRowCols); updateWidget(WidgetOperations.RESIZE, props.widgetId, newRowCols);
} }
// Clear border styles // Clear border styles
setIsColliding && setIsColliding(false); // setIsColliding && setIsColliding(false);
// Tell the Canvas that we've stopped resizing // Tell the Canvas that we've stopped resizing
// Put it laster in the stack so that other updates like click, are not propagated to the parent container // Put it laster in the stack so that other updates like click, are not propagated to the parent container
setTimeout(() => { setTimeout(() => {
@ -177,53 +308,38 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
// Let the propertypane show. // Let the propertypane show.
// The propertypane decides whether to show itself, based on // The propertypane decides whether to show itself, based on
// whether it was showing when the widget resize started. // whether it was showing when the widget resize started.
showPropertyPane &&
propertyPaneState.widgetId !== props.widgetId &&
showPropertyPane(props.widgetId, true);
};
const handleResizeStart = () => {
setIsResizing && !isResizing && setIsResizing(true);
selectWidget &&
selectedWidget !== props.widgetId &&
selectWidget(props.widgetId);
showPropertyPane && showPropertyPane(props.widgetId, true); showPropertyPane && showPropertyPane(props.widgetId, true);
}; };
const style = getBorderStyles(
isWidgetFocused,
isColliding,
props.paddingOffset - 2,
);
return ( return (
<ResizableContainer <Resizable
isfocused={isWidgetFocused ? "true" : undefined} handles={{
position={{ left: LeftHandleStyles,
x: 0, top: TopHandleStyles,
y: 0, bottom: BottomHandleStyles,
}} right: RightHandleStyles,
size={dimensions} bottomRight: BottomRightHandleStyles,
disableDragging
minWidth={props.parentColumnSpace}
minHeight={props.parentRowSpace}
style={style}
onResizeStop={updateSize}
onResize={checkForCollision}
onResizeStart={(e: any) => {
setIsResizing && setIsResizing(true);
selectWidget && selectWidget(props.widgetId);
showPropertyPane && showPropertyPane(undefined, true);
}}
resizeGrid={[props.parentColumnSpace, props.parentRowSpace]}
bounds={bounds}
resizeHandleStyles={getHandleSyles()}
enableResizing={{
top: canResize,
right: canResize,
bottom: canResize,
left: canResize,
topRight: canResize,
topLeft: canResize,
bottomRight: canResize,
bottomLeft: canResize,
}} }}
componentHeight={dimensions.height}
componentWidth={dimensions.width}
onStart={handleResizeStart}
onStop={updateSize}
snapGrid={{ x: props.parentColumnSpace, y: props.parentRowSpace }}
enable={!isDragging && isWidgetFocused}
isColliding={isColliding}
> >
<ResizeBorderDotDiv {props.children}
isfocused={isWidgetFocused} </Resizable>
visible={!!props.isVisible}
>
{props.children}
</ResizeBorderDotDiv>
</ResizableContainer>
); );
}); });

View File

@ -1,59 +0,0 @@
import { Rnd } from "react-rnd";
import styled, { css } from "styled-components";
import { ContainerWidgetProps } from "widgets/ContainerWidget";
import { WidgetProps } from "widgets/BaseWidget";
import { invisible } from "constants/DefaultTheme";
export type ResizableComponentProps = ContainerWidgetProps<WidgetProps> & {
paddingOffset: number;
};
interface ResizeBorderDotDivProps {
isfocused: boolean;
visible: boolean;
}
const borderCSS = css<ResizeBorderDotDivProps>`
position: relative;
opacity: 0.99;
height: 100%;
&: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};
}
`;
export const ResizeBorderDotDiv = styled.div<ResizeBorderDotDivProps>`
${props => (!props.visible ? invisible : "")}
${borderCSS}
&:after {
left: -${props => props.theme.spaces[2]}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[2]}px;
z-index: 1;
}
`;
export default 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;
}
`;

View File

@ -1,168 +1,61 @@
import { CSSProperties } from "react";
import { XYCoord } from "react-dnd"; import { XYCoord } from "react-dnd";
import { theme } from "constants/DefaultTheme";
import { WidgetProps, WidgetRowCols } from "widgets/BaseWidget"; import { WidgetProps, WidgetRowCols } from "widgets/BaseWidget";
import { isDropZoneOccupied } from "utils/WidgetPropsUtils";
import { OccupiedSpace } from "constants/editorConstants";
import { GridDefaults } from "constants/WidgetConstants"; import { GridDefaults } from "constants/WidgetConstants";
export type UIElementSize = { height: number; width: number }; export type UIElementSize = { height: number; width: number };
export const RESIZABLE_CONTAINER_BORDER_THEME_INDEX = 1; export const RESIZABLE_CONTAINER_BORDER_THEME_INDEX = 1;
const RESIZE_HANDLE_HOVER_AREA_WIDTH = 12; export const computeRowCols = (
export const getHandleSyles = (): {
top: CSSProperties;
bottom: CSSProperties;
right: CSSProperties;
left: CSSProperties;
bottomRight: CSSProperties;
bottomLeft: CSSProperties;
} => {
const hoverWidth = RESIZE_HANDLE_HOVER_AREA_WIDTH;
const hoverWidthHalf = hoverWidth / 2;
const halfBorder =
theme.borders[RESIZABLE_CONTAINER_BORDER_THEME_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",
},
};
};
export const computeUpdatedRowCols = (
isColliding: boolean,
delta: UIElementSize, delta: UIElementSize,
position: XYCoord, position: XYCoord,
props: WidgetProps, props: WidgetProps,
): WidgetRowCols | false => { ) => {
if (isColliding) return false; return {
const newRowCols: WidgetRowCols = { leftColumn: Math.round(
leftColumn: Math.max( props.leftColumn + position.x / props.parentColumnSpace,
Math.round(props.leftColumn + position.x / props.parentColumnSpace),
0,
), ),
topRow: Math.round(props.topRow + position.y / props.parentRowSpace), topRow: Math.round(props.topRow + position.y / props.parentRowSpace),
rightColumn: Math.round(
rightColumn: Math.min( props.rightColumn + (delta.width + position.x) / props.parentColumnSpace,
Math.round(
props.rightColumn +
(delta.width + position.x) / props.parentColumnSpace,
),
GridDefaults.DEFAULT_GRID_COLUMNS,
), ),
bottomRow: Math.round( bottomRow: Math.round(
props.bottomRow + (delta.height + position.y) / props.parentRowSpace, props.bottomRow + (delta.height + position.y) / props.parentRowSpace,
), ),
}; };
};
if ( export const computeBoundedRowCols = (rowCols: WidgetRowCols) => {
return {
leftColumn: Math.max(rowCols.leftColumn, 0),
rightColumn: Math.min(
rowCols.rightColumn,
GridDefaults.DEFAULT_GRID_COLUMNS,
),
topRow: rowCols.topRow,
bottomRow: rowCols.bottomRow,
};
};
export const hasRowColsChanged = (
newRowCols: WidgetRowCols,
props: WidgetProps,
) => {
return (
props.leftColumn !== newRowCols.leftColumn || props.leftColumn !== newRowCols.leftColumn ||
props.topRow !== newRowCols.topRow || props.topRow !== newRowCols.topRow ||
props.bottomRow !== newRowCols.bottomRow || props.bottomRow !== newRowCols.bottomRow ||
props.rightColumn !== newRowCols.rightColumn props.rightColumn !== newRowCols.rightColumn
) {
return newRowCols;
}
return false;
};
export const hasCollision = (
delta: UIElementSize,
position: XYCoord,
props: WidgetProps,
occupiedSpaces?: OccupiedSpace[],
maxBottomRow?: number,
): boolean => {
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 (maxBottomRow && bottom - top - 1 < maxBottomRow) {
return true;
}
return isDropZoneOccupied(
{
left: Math.round(left),
top: Math.round(top),
bottom: Math.round(bottom),
right: Math.round(right),
},
props.widgetId,
occupiedSpaces,
); );
}; };
// TODO(abhinav): Memoize this? export const computeFinalRowCols = (
export const getBorderStyles = ( delta: UIElementSize,
isWidgetFocused: boolean, position: XYCoord,
isColliding: boolean, props: WidgetProps,
padding: number, ): WidgetRowCols | false => {
): CSSProperties => { const newRowCols = computeBoundedRowCols(
const selectedColor = theme.colors.widgetBorder; computeRowCols(delta, position, props),
const collisionColor = theme.colors.error; );
return hasRowColsChanged(newRowCols, props) ? newRowCols : false;
// To fix the widget select/unselect size descripancy: Issue #127
// Always have the border, just toggle the opacity.
const unselectedColor = "transparent";
const borderThickness =
theme.borders[RESIZABLE_CONTAINER_BORDER_THEME_INDEX].thickness + "px";
const borderStyle =
theme.borders[RESIZABLE_CONTAINER_BORDER_THEME_INDEX].style;
let borderColor = unselectedColor;
if (isWidgetFocused) {
borderColor = selectedColor;
}
if (isColliding && isWidgetFocused) {
borderColor = collisionColor;
}
return {
border: [borderThickness, borderStyle, borderColor].join(" "),
padding: padding + "px",
};
}; };

View File

@ -0,0 +1,211 @@
import React, { ReactNode, useState, useEffect } from "react";
import styled, { FlattenSimpleInterpolation } from "styled-components";
import { useDrag } from "react-use-gesture";
import { Spring } from "react-spring/renderprops";
const ResizeWrapper = styled.div`
position: absolute;
display: block;
`;
const getSnappedValues = (
x: number,
y: number,
snapGrid: { x: number; y: number },
) => {
return {
x: Math.round(x / snapGrid.x) * snapGrid.x,
y: Math.round(y / snapGrid.y) * snapGrid.y,
};
};
type ResizableHandleProps = {
dragCallback: (x: number, y: number) => void;
component: FlattenSimpleInterpolation;
onStart: Function;
onStop: Function;
snapGrid: {
x: number;
y: number;
};
};
const ResizableHandle = (props: ResizableHandleProps) => {
const bind = useDrag(
({ first, last, dragging, movement: [mx, my], memo }) => {
const snapped = getSnappedValues(mx, my, props.snapGrid);
if (dragging && memo && (snapped.x !== memo.x || snapped.y !== memo.y)) {
props.dragCallback(snapped.x, snapped.y);
}
if (first) {
props.onStart();
}
if (last) {
props.onStop();
}
return snapped;
},
);
const HandleComponent = styled.div`
${props.component}
`;
return <HandleComponent {...bind()} />;
};
type ResizableProps = {
handles: {
left: FlattenSimpleInterpolation;
top: FlattenSimpleInterpolation;
bottom: FlattenSimpleInterpolation;
right: FlattenSimpleInterpolation;
bottomRight: FlattenSimpleInterpolation;
};
componentWidth: number;
componentHeight: number;
children: ReactNode;
onStart: Function;
onStop: Function;
snapGrid: { x: number; y: number };
enable: boolean;
isColliding: Function;
};
export const Resizable = (props: ResizableProps) => {
const [newDimensions, set] = useState({
width: props.componentWidth,
height: props.componentHeight,
x: 0,
y: 0,
});
const setNewDimensions = (rect: {
width: number;
height: number;
x: number;
y: number;
}) => {
const { width, height, x, y } = rect;
const isColliding = props.isColliding({ width, height }, { x, y });
if (!isColliding) {
set(rect);
}
};
useEffect(() => {
set({
width: props.componentWidth,
height: props.componentHeight,
x: 0,
y: 0,
});
}, [props.componentHeight, props.componentWidth]);
const handles = [
{
dragCallback: (x: number) => {
setNewDimensions({
width: props.componentWidth - x,
height: newDimensions.height,
x,
y: newDimensions.y,
});
},
component: props.handles.left,
},
{
dragCallback: (x: number) => {
setNewDimensions({
width: props.componentWidth + x,
height: newDimensions.height,
x: newDimensions.x,
y: newDimensions.y,
});
},
component: props.handles.right,
},
{
dragCallback: (x: number, y: number) => {
setNewDimensions({
width: newDimensions.width,
height: props.componentHeight - y,
y: y,
x: newDimensions.x,
});
},
component: props.handles.top,
},
{
dragCallback: (x: number, y: number) => {
setNewDimensions({
width: newDimensions.width,
height: props.componentHeight + y,
x: newDimensions.x,
y: newDimensions.y,
});
},
component: props.handles.bottom,
},
{
dragCallback: (x: number, y: number) => {
setNewDimensions({
width: props.componentWidth + x,
height: props.componentHeight + y,
x: newDimensions.x,
y: newDimensions.y,
});
},
component: props.handles.bottomRight,
},
];
const onResizeStop = () => {
props.onStop(
{
width: newDimensions.width,
height: newDimensions.height,
},
{
x: newDimensions.x,
y: newDimensions.y,
},
);
};
const renderHandles = handles.map((handle, index) => (
<ResizableHandle
{...handle}
key={index}
onStart={props.onStart}
onStop={onResizeStop}
snapGrid={props.snapGrid}
/>
));
return (
<Spring
from={{
width: props.componentWidth,
height: props.componentHeight,
}}
to={{
width: newDimensions.width,
height: newDimensions.height,
transform: `translate3d(${newDimensions.x}px,${newDimensions.y}px,0)`,
}}
config={{
clamp: true,
friction: 0,
tension: 999,
}}
>
{_props => (
<ResizeWrapper style={_props}>
{props.children}
{props.enable && renderHandles}
</ResizeWrapper>
)}
</Spring>
);
};
export default Resizable;

View File

@ -1,7 +1,7 @@
import generate from "nanoid/generate"; import generate from "nanoid/generate";
const ALPHANUMERIC = "1234567890abcdefghijklmnopqrstuvwxyz"; const ALPHANUMERIC = "1234567890abcdefghijklmnopqrstuvwxyz";
const ALPHABET = "abcdefghijklmnopqrstuvwxyz"; // const ALPHABET = "abcdefghijklmnopqrstuvwxyz";
export const generateReactKey = ({ export const generateReactKey = ({
prefix = "", prefix = "",
@ -10,8 +10,7 @@ export const generateReactKey = ({
}; };
export const generateClassName = (seed?: string) => { export const generateClassName = (seed?: string) => {
if (!seed) return generate(ALPHABET, 7); return `_${seed}`;
return seed.replace(/^\d+\s*/, "_");
}; };
export default { export default {

View File

@ -6781,11 +6781,6 @@ fast-levenshtein@~2.0.6:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
fast-memoize@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/fast-memoize/-/fast-memoize-2.5.1.tgz#c3519241e80552ce395e1a32dcdde8d1fd680f5d"
integrity sha512-xdmw296PCL01tMOXx9mdJSmWY29jQgxyuZdq0rEHMu+Tpe1eOEtCycoG6chzlcrWsNgpZP7oL8RiQr7+G6Bl6g==
fastq@^1.6.0: fastq@^1.6.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2"
@ -12505,13 +12500,6 @@ re-reselect@^3.4.0:
resolved "https://registry.yarnpkg.com/re-reselect/-/re-reselect-3.4.0.tgz#0f2303f3c84394f57f0cd31fea08a1ca4840a7cd" resolved "https://registry.yarnpkg.com/re-reselect/-/re-reselect-3.4.0.tgz#0f2303f3c84394f57f0cd31fea08a1ca4840a7cd"
integrity sha512-JsecfN+JlckncVXTWFWjn0Vk6uInl8GSf4eEd9tTk5qXHlgqkPdILpnYpgZcISXNYAzvfvsCZviaDk8AxyS5sg== integrity sha512-JsecfN+JlckncVXTWFWjn0Vk6uInl8GSf4eEd9tTk5qXHlgqkPdILpnYpgZcISXNYAzvfvsCZviaDk8AxyS5sg==
re-resizable@6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.1.1.tgz#7ff7cfe92c0b9d8b0bceaa578aadaeeff8931eaf"
integrity sha512-ngzX5xbXi9LlIghJUYZaBDkJUIMLYqO3tQ2cJZoNprCRGhfHnbyufKm51MZRIOBlLigLzPPFKBxQE8ZLezKGfA==
dependencies:
fast-memoize "^2.5.1"
react-app-polyfill@^1.0.6: react-app-polyfill@^1.0.6:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz#890f8d7f2842ce6073f030b117de9130a5f385f0" resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz#890f8d7f2842ce6073f030b117de9130a5f385f0"
@ -12701,7 +12689,7 @@ react-dom@^16.7.0, react-dom@^16.8.3:
prop-types "^15.6.2" prop-types "^15.6.2"
scheduler "^0.18.0" scheduler "^0.18.0"
react-draggable@4.2.0, react-draggable@^4.0.3: react-draggable@^4.0.3:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.2.0.tgz#40cc5209082ca7d613104bf6daf31372cc0e1114" resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.2.0.tgz#40cc5209082ca7d613104bf6daf31372cc0e1114"
integrity sha512-5wFq//gEoeTYprnd4ze8GrFc+Rbnx+9RkOMR3vk4EbWxj02U6L6T3yrlKeiw4X5CtjD2ma2+b3WujghcXNRzkw== integrity sha512-5wFq//gEoeTYprnd4ze8GrFc+Rbnx+9RkOMR3vk4EbWxj02U6L6T3yrlKeiw4X5CtjD2ma2+b3WujghcXNRzkw==
@ -12837,15 +12825,6 @@ react-redux@^7.1.1, react-redux@^7.1.3:
prop-types "^15.7.2" prop-types "^15.7.2"
react-is "^16.9.0" react-is "^16.9.0"
react-rnd@^10.1.1:
version "10.1.5"
resolved "https://registry.yarnpkg.com/react-rnd/-/react-rnd-10.1.5.tgz#75b4acfa19243bf41843e71b2e926e4b129e3313"
integrity sha512-NCGc62bD1xqS6H+3j0yWfo6Y1btO4136zgphyUvCsY9xfgsWfKY7kWLGxH/vw2jP9MWlAD/sBqyJ1vywcIH6zw==
dependencies:
re-resizable "6.1.1"
react-draggable "4.2.0"
tslib "1.10.0"
react-router-dom@^5.1.2: react-router-dom@^5.1.2:
version "5.1.2" version "5.1.2"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18"
@ -13041,6 +13020,11 @@ react-transition-group@^4, react-transition-group@^4.3.0:
loose-envify "^1.4.0" loose-envify "^1.4.0"
prop-types "^15.6.2" prop-types "^15.6.2"
react-use-gesture@^7.0.4:
version "7.0.4"
resolved "https://registry.yarnpkg.com/react-use-gesture/-/react-use-gesture-7.0.4.tgz#94493ede7d90c0e05a97b366e419b55eaea53739"
integrity sha512-6RGHBRyHXSqRwti/osj7WTcSVTxeulyz+pLg20vCv/bl1oEU3mVVEtmbPpsXWsqR23FZuE7//8AiqVmQQSGuBg==
react-virtualized-auto-sizer@^1.0.2: react-virtualized-auto-sizer@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.2.tgz#a61dd4f756458bbf63bd895a92379f9b70f803bd" resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.2.tgz#a61dd4f756458bbf63bd895a92379f9b70f803bd"
@ -15165,7 +15149,7 @@ ts-pnp@1.1.5, ts-pnp@^1.1.2:
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.5.tgz#840e0739c89fce5f3abd9037bb091dbff16d9dec" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.5.tgz#840e0739c89fce5f3abd9037bb091dbff16d9dec"
integrity sha512-ti7OGMOUOzo66wLF3liskw6YQIaSsBgc4GOAlWRnIEj8htCxJUxskanMUoJOD6MDCRAXo36goXJZch+nOS0VMA== integrity sha512-ti7OGMOUOzo66wLF3liskw6YQIaSsBgc4GOAlWRnIEj8htCxJUxskanMUoJOD6MDCRAXo36goXJZch+nOS0VMA==
tslib@1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
version "1.10.0" version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==