Optimize popper creation and destructions. Preserve property pane visibility before and after actions.

This commit is contained in:
Abhinav Jha 2020-01-07 17:58:58 +05:30
parent 40d160fb78
commit 9d7eaaca80
8 changed files with 61 additions and 27 deletions

View File

@ -1,7 +1,8 @@
import React, { useContext } from "react";
import styled from "styled-components";
import { FocusContext } from "pages/Editor/CanvasContexts";
import { FocusContext, ResizingContext } from "pages/Editor/CanvasContexts";
import { DraggableComponentContext } from "components/editorComponents/DraggableComponent";
const PositionStyle = styled.div<{ selected?: boolean }>`
position: absolute;
top: -${props => props.theme.spaces[10]}px;
@ -30,10 +31,12 @@ type WidgetNameComponentProps = {
export const WidgetNameComponent = (props: WidgetNameComponentProps) => {
const { focusedWidget, selectedWidget } = useContext(FocusContext);
const { isDragging } = useContext(DraggableComponentContext);
return (focusedWidget === props.widgetId ||
selectedWidget === props.widgetId) &&
!isDragging ? (
const { isResizing } = useContext(ResizingContext);
const showWidgetName =
(focusedWidget === props.widgetId || selectedWidget === props.widgetId) &&
!isDragging &&
!isResizing;
return showWidgetName ? (
<PositionStyle selected={selectedWidget === props.widgetId}>
<pre>{props.widgetName}</pre>
</PositionStyle>

View File

@ -127,12 +127,12 @@ const DraggableComponent = (props: DraggableComponentProps) => {
isDragging: monitor.isDragging(),
}),
begin: () => {
showPropertyPane && showPropertyPane();
showPropertyPane && showPropertyPane(undefined, true);
selectWidget && selectWidget(props.widgetId);
},
end: (widget, monitor) => {
if (monitor.didDrop()) {
showPropertyPane && showPropertyPane(props.widgetId);
showPropertyPane && showPropertyPane(props.widgetId, true);
}
},
canDrag: () => {

View File

@ -9,12 +9,6 @@ import { FocusContext, ResizingContext } from "pages/Editor/CanvasContexts";
import DragLayerComponent from "./DragLayerComponent";
/*
TODO(abhinav):
1) Drag collision is not working
2) Dragging into a new container does not work
*/
type DropTargetComponentProps = WidgetProps & {
children?: ReactNode;
snapColumnSpace: number;
@ -52,8 +46,10 @@ 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);
updateWidget &&
@ -106,7 +102,7 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
};
const handleFocus = () => {
if (!props.parentId) {
if (!props.parentId && !isResizing) {
selectWidget && selectWidget(props.widgetId);
showPropertyPane && showPropertyPane();
}

View File

@ -120,14 +120,16 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
// Clear border styles
setIsColliding && setIsColliding(false);
// Tell the Canvas that we've stopped resizing
setIsResizing && setIsResizing(false);
setTimeout(() => {
setIsResizing && setIsResizing(false);
}, 300);
// Tell the Canvas to put the focus back to this widget
// By setting the focus, we enable the control buttons on the widget
selectWidget && selectWidget(props.widgetId);
// Let the propertypane show.
// The propertypane decides whether to show itself, based on
// whether it was showing when the widget resize started.
showPropertyPane && showPropertyPane(props.widgetId);
showPropertyPane && showPropertyPane(props.widgetId, true);
};
const style = getBorderStyles(
isWidgetFocused,
@ -148,10 +150,12 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
style={style}
onResizeStop={updateSize}
onResize={checkForCollision}
onResizeStart={() => {
onResizeStart={(e: any) => {
setIsResizing && setIsResizing(true);
selectWidget && selectWidget(props.widgetId);
showPropertyPane && showPropertyPane();
showPropertyPane && showPropertyPane(undefined, true);
e.preventDefault();
e.stopPropagation();
}}
resizeGrid={[props.parentColumnSpace, props.parentRowSpace]}
bounds={bounds}

View File

@ -1,5 +1,4 @@
import { WidgetProps, WidgetCardProps } from "widgets/BaseWidget";
import { RefObject } from "react";
import { PageAction } from "constants/ActionConstants";
export const ReduxActionTypes: { [key: string]: string } = {
@ -220,7 +219,6 @@ export interface UpdateCanvasPayload {
export interface ShowPropertyPanePayload {
widgetId: string;
node: RefObject<HTMLDivElement>;
toggle: boolean;
}

View File

@ -23,7 +23,6 @@ const PopperWrapper = styled(PaneWrapper)`
export default (props: PopperProps) => {
const contentRef = useRef(null);
useEffect(() => {
//TODO(abhinav): optimize this, remove previous Popper instance.
const parentElement = props.targetNode && props.targetNode.parentElement;
if (
parentElement &&
@ -31,7 +30,7 @@ export default (props: PopperProps) => {
props.targetNode &&
props.isOpen
) {
new PopperJS(
const _popper = new PopperJS(
props.targetNode,
(contentRef.current as unknown) as Element,
{
@ -49,6 +48,10 @@ export default (props: PopperProps) => {
},
},
);
_popper.disableEventListeners();
return () => {
_popper.destroy();
};
}
}, [props.targetNode, props.isOpen]);
return createPortal(

View File

@ -100,8 +100,16 @@ const mapDispatchToProps = (dispatch: any) => {
dsl: ContainerWidgetProps<WidgetProps>,
) => dispatch(savePage(pageId, layoutId, dsl)),
showPropertyPane: (widgetId?: string, toggle = false) => {
// If widgetId is not provided, we don't show the property pane.
// However, if toggle is provided, it will be a start or end of an action
// toggle payload is handled in SHOW_PROPERTY_PANE action.
// Ergo, when eithter widgetId or toggle are provided, SHOW_PROPERTY_PANE
// Else, HIDE_PROPERTY_PANE
dispatch({
type: ReduxActionTypes.SHOW_PROPERTY_PANE,
type:
widgetId || toggle
? ReduxActionTypes.SHOW_PROPERTY_PANE
: ReduxActionTypes.HIDE_PROPERTY_PANE,
payload: { widgetId, toggle },
});
},

View File

@ -15,17 +15,39 @@ const propertyPaneReducer = createReducer(initialState, {
state: PropertyPaneReduxState,
action: ReduxAction<ShowPropertyPanePayload>,
) => {
const { widgetId } = action.payload;
return { ...state, widgetId, isVisible: true };
const { widgetId, toggle } = action.payload;
// If toggle is true, an action has started or ended.
// If the action has started, isVisibleBeforeAction should be undefined
// If the action has ended, isVisibleBeforeAction should be the visible state
// of the property pane to use.
let isVisibleBeforeAction = undefined;
if (toggle && state.isVisibleBeforeAction === undefined) {
isVisibleBeforeAction = state.isVisible;
}
// If toggle is true, an action has started or ended
// If isVisibleBeforeAction is undefined, show property pane
// If isVisibleBeforeAction is defined, set visibility to its value
let isVisible = true;
if (toggle && state.isVisibleBeforeAction === undefined) {
isVisible = false;
} else if (toggle && state.isVisibleBeforeAction !== undefined) {
isVisible = state.isVisibleBeforeAction;
} else {
isVisible = true;
}
return { ...state, widgetId, isVisible, isVisibleBeforeAction };
},
[ReduxActionTypes.HIDE_PROPERTY_PANE]: (state: PropertyPaneReduxState) => {
return { ...state, isVisible: false };
return { ...state, isVisible: false, isVisibleBeforeAction: undefined };
},
});
export interface PropertyPaneReduxState {
widgetId?: string;
isVisible: boolean;
isVisibleBeforeAction?: boolean;
}
export default propertyPaneReducer;