Property pane should open on move, add and resize

This commit is contained in:
Abhinav Jha 2020-01-02 18:12:02 +05:30
parent 3e88ae0074
commit 189c380e85
13 changed files with 50 additions and 91 deletions

View File

@ -98,6 +98,7 @@ export type WidgetAddChild = {
rows: number;
parentRowSpace: number;
parentColumnSpace: number;
newWidgetId: string;
};
export type WidgetMove = {

View File

@ -1,10 +1,4 @@
import React, {
useContext,
createContext,
useState,
Context,
useCallback,
} from "react";
import React, { useContext, createContext, Context } from "react";
import styled from "styled-components";
import { WidgetProps, WidgetOperations } from "widgets/BaseWidget";
import { ContainerWidgetProps } from "widgets/ContainerWidget";
@ -14,6 +8,8 @@ import { FocusContext, ResizingContext } from "pages/Editor/CanvasContexts";
import { EditorContext } from "components/editorComponents/EditorContextProvider";
import { ControlIcons } from "icons/ControlIcons";
import { Tooltip } from "@blueprintjs/core";
import { WIDGET_CLASSNAME_PREFIX } from "constants/WidgetConstants";
import { IntentColors } from "constants/DefaultTheme";
// FontSizes array in DefaultTheme.tsx
// Change this to toggle the size of delete and move handles.
@ -54,15 +50,6 @@ const EditControl = styled.div`
cursor: pointer;
`;
const DraggableMask = styled.div`
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: -1;
`;
const CONTROL_ICON_SIZE = 20;
const moveControlIcon = ControlIcons.MOVE_CONTROL({
@ -75,16 +62,10 @@ const deleteControlIcon = ControlIcons.DELETE_CONTROL({
height: CONTROL_ICON_SIZE,
});
const editControlIcon = ControlIcons.EDIT_CONTROL({
width: CONTROL_ICON_SIZE,
height: CONTROL_ICON_SIZE,
});
type DraggableComponentProps = ContainerWidgetProps<WidgetProps>;
export const DraggableComponentContext: Context<{
isDragging?: boolean;
widgetNode?: HTMLDivElement;
}> = createContext({});
/* eslint-disable react/display-name */
@ -95,17 +76,15 @@ const DraggableComponent = (props: DraggableComponentProps) => {
showPropertyPane,
propertyPaneWidgetId,
} = useContext(FocusContext);
const editControlIcon = ControlIcons.EDIT_CONTROL({
width: CONTROL_ICON_SIZE,
height: CONTROL_ICON_SIZE,
background:
propertyPaneWidgetId === props.widgetId ? IntentColors.primary : "auto",
});
const { updateWidget } = useContext(EditorContext);
const [currentNode, setCurrentNode] = useState<HTMLDivElement>();
const referenceRef = useCallback(
node => {
if (node !== null && node !== currentNode) {
setCurrentNode(node);
}
},
[setCurrentNode, currentNode],
);
const { isResizing } = useContext(ResizingContext);
const deleteWidget = () => {
@ -117,8 +96,8 @@ const DraggableComponent = (props: DraggableComponentProps) => {
};
const togglePropertyEditor = (e: any) => {
if (showPropertyPane && props.widgetId && currentNode) {
showPropertyPane(props.widgetId, currentNode, true);
if (showPropertyPane && props.widgetId) {
showPropertyPane(props.widgetId, true);
}
e.stopPropagation();
};
@ -129,14 +108,14 @@ const DraggableComponent = (props: DraggableComponentProps) => {
isDragging: monitor.isDragging(),
}),
begin: () => {
if (showPropertyPane && currentNode) {
showPropertyPane(props.widgetId, undefined);
if (showPropertyPane) {
showPropertyPane(props.widgetId);
}
},
end: (widget, monitor) => {
if (monitor.didDrop()) {
if (showPropertyPane && currentNode) {
showPropertyPane(props.widgetId, currentNode);
if (showPropertyPane) {
showPropertyPane(props.widgetId);
}
}
},
@ -146,12 +125,10 @@ const DraggableComponent = (props: DraggableComponentProps) => {
});
return (
<DraggableComponentContext.Provider
value={{ isDragging, widgetNode: currentNode }}
>
<DraggableComponentContext.Provider value={{ isDragging }}>
<DragPreviewImage connect={preview} src={blankImage} />
<DraggableWrapper
className={props.widgetId}
className={WIDGET_CLASSNAME_PREFIX + props.widgetId}
ref={drag}
onMouseOver={(e: any) => {
if (setFocus) {
@ -171,7 +148,7 @@ const DraggableComponent = (props: DraggableComponentProps) => {
}}
onDoubleClick={(e: any) => {
setFocus && setFocus(props.widgetId);
showPropertyPane && showPropertyPane(props.widgetId, currentNode);
showPropertyPane && showPropertyPane(props.widgetId);
e.stopPropagation();
}}
show={props.widgetId === isFocused && !isResizing}
@ -186,7 +163,6 @@ const DraggableComponent = (props: DraggableComponentProps) => {
userSelect: "none",
}}
>
<DraggableMask ref={referenceRef} />
{props.children}
<DragHandle className="control" ref={drag}>
<Tooltip content="Move" hoverOpenDelay={500}>

View File

@ -53,6 +53,9 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
props.widgetId,
);
showPropertyPane &&
showPropertyPane(updateWidgetParams.payload.newWidgetId);
updateWidget &&
updateWidget(
updateWidgetParams.operation,

View File

@ -23,7 +23,7 @@ import {
/* eslint-disable react/display-name */
export const ResizableComponent = memo((props: ResizableComponentProps) => {
// Fetch information from the context
const { isDragging, widgetNode } = useContext(DraggableComponentContext);
const { isDragging } = useContext(DraggableComponentContext);
const { setIsResizing } = useContext(ResizingContext);
const { updateWidget, occupiedSpaces } = useContext(EditorContext);
const { showPropertyPane, isFocused, setFocus } = useContext(FocusContext);
@ -121,7 +121,7 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
// 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, widgetNode);
showPropertyPane && showPropertyPane(props.widgetId);
};
const style = getBorderStyles(
isWidgetFocused,

View File

@ -9,11 +9,7 @@ import { ResizingContext, FocusContext } from "./CanvasContexts";
interface CanvasProps {
dsl: ContainerWidgetProps<WidgetProps>;
showPropertyPane: (
widgetId?: string,
node?: HTMLDivElement,
toggle?: boolean,
) => void;
showPropertyPane: (widgetId?: string, toggle?: boolean) => void;
propertyPaneWidgetId?: string;
}

View File

@ -4,11 +4,7 @@ export const FocusContext: Context<{
isFocused?: string;
setFocus?: Function;
propertyPaneWidgetId?: string;
showPropertyPane?: (
widgetId?: string,
node?: HTMLDivElement,
toggle?: boolean,
) => void;
showPropertyPane?: (widgetId?: string, toggle?: boolean) => void;
}> = createContext({});
export const ResizingContext: Context<{

View File

@ -6,7 +6,7 @@ import PaneWrapper from "pages/common/PaneWrapper";
type PopperProps = {
isOpen: boolean;
targetRefNode?: HTMLDivElement;
targetNode?: Element;
children: JSX.Element;
};
@ -24,16 +24,15 @@ export default (props: PopperProps) => {
const contentRef = useRef(null);
useEffect(() => {
//TODO(abhinav): optimize this, remove previous Popper instance.
const parentElement =
props.targetRefNode && props.targetRefNode.parentElement;
const parentElement = props.targetNode && props.targetNode.parentElement;
if (
parentElement &&
parentElement.parentElement &&
props.targetRefNode &&
props.targetNode &&
props.isOpen
) {
new PopperJS(
props.targetRefNode,
props.targetNode,
(contentRef.current as unknown) as Element,
{
placement: "right-start",
@ -51,7 +50,7 @@ export default (props: PopperProps) => {
},
);
}
}, [props.targetRefNode, props.isOpen]);
}, [props.targetNode, props.isOpen]);
return createPortal(
<PopperWrapper ref={contentRef}>{props.children}</PopperWrapper>,
document.body,

View File

@ -8,7 +8,6 @@ import { PropertySection } from "reducers/entityReducers/propertyPaneConfigReduc
import { updateWidgetProperty } from "actions/controlActions";
import {
getCurrentWidgetId,
getCurrentReferenceNode,
getPropertyConfig,
getIsPropertyPaneVisible,
getWidgetPropsWithValidations,
@ -17,7 +16,10 @@ import { Divider } from "@blueprintjs/core";
import Popper from "./Popper";
import { ControlProps } from "components/propertyControls/BaseControl";
import { RenderModes } from "constants/WidgetConstants";
import {
RenderModes,
WIDGET_CLASSNAME_PREFIX,
} from "constants/WidgetConstants";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { CloseButton } from "components/designSystems/blueprint/CloseButton";
import { theme } from "constants/DefaultTheme";
@ -58,8 +60,11 @@ class PropertyPane extends Component<
render() {
if (this.props.isVisible) {
const content = this.renderPropertyPane(this.props.propertySections);
const el = document.getElementsByClassName(
WIDGET_CLASSNAME_PREFIX + this.props.widgetId,
)[0];
return (
<Popper isOpen={true} targetRefNode={this.props.targetNode}>
<Popper isOpen={true} targetNode={el}>
{content}
</Popper>
);
@ -162,7 +167,6 @@ const mapStateToProps = (state: AppState): PropertyPaneProps => {
widgetId: getCurrentWidgetId(state),
widgetProperties: getWidgetPropsWithValidations(state),
isVisible: getIsPropertyPaneVisible(state),
targetNode: getCurrentReferenceNode(state),
};
};
@ -193,7 +197,6 @@ export interface PropertyPaneProps {
widgetId?: string;
widgetProperties?: WidgetProps; //TODO(abhinav): Secure type definition
isVisible: boolean;
targetNode?: HTMLDivElement;
}
export interface PropertyPaneFunctions {

View File

@ -47,11 +47,7 @@ const CanvasContainer = styled.section`
type EditorProps = {
dsl?: ContainerWidgetProps<WidgetProps>;
savePageLayout: Function;
showPropertyPane: (
widgetId?: string,
node?: HTMLDivElement,
toggle?: boolean,
) => void;
showPropertyPane: (widgetId?: string, toggle?: boolean) => void;
fetchPage: (pageId: string) => void;
currentPageId?: string;
isFetchingPage: boolean;
@ -112,14 +108,10 @@ const mapDispatchToProps = (dispatch: any) => {
layoutId: string,
dsl: ContainerWidgetProps<WidgetProps>,
) => dispatch(savePage(pageId, layoutId, dsl)),
showPropertyPane: (
widgetId?: string,
node?: HTMLDivElement,
toggle = false,
) => {
showPropertyPane: (widgetId?: string, toggle = false) => {
dispatch({
type: ReduxActionTypes.SHOW_PROPERTY_PANE,
payload: { widgetId, node, toggle },
payload: { widgetId, toggle },
});
},
fetchPage: (pageId: string) => dispatch(fetchPage(pageId)),

View File

@ -8,7 +8,6 @@ import {
const initialState: PropertyPaneReduxState = {
isVisible: false,
widgetId: undefined,
node: undefined,
};
const propertyPaneReducer = createReducer(initialState, {
@ -16,8 +15,8 @@ const propertyPaneReducer = createReducer(initialState, {
state: PropertyPaneReduxState,
action: ReduxAction<ShowPropertyPanePayload>,
) => {
const { widgetId, node } = action.payload;
return { ...state, widgetId, node, isVisible: true };
const { widgetId } = action.payload;
return { ...state, widgetId, isVisible: true };
},
[ReduxActionTypes.HIDE_PROPERTY_PANE]: (state: PropertyPaneReduxState) => {
return { ...state, isVisible: false };
@ -27,7 +26,6 @@ const propertyPaneReducer = createReducer(initialState, {
export interface PropertyPaneReduxState {
widgetId?: string;
isVisible: boolean;
node?: HTMLDivElement;
}
export default propertyPaneReducer;

View File

@ -33,6 +33,7 @@ export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
rows,
parentRowSpace,
parentColumnSpace,
newWidgetId,
} = addChildAction.payload;
const widget: FlattenedWidgetProps = yield select(getWidget, widgetId);
const widgets = yield select(getWidgets);
@ -49,6 +50,7 @@ export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
getNextWidgetName(defaultWidgetConfig.widgetName, widgets),
defaultWidgetConfig,
);
childWidget.widgetId = newWidgetId;
widgets[childWidget.widgetId] = childWidget;
if (widget && widget.children) {
widget.children.push(childWidget.widgetId);

View File

@ -52,13 +52,6 @@ export const getWidgetPropsWithValidations = createSelector(
},
);
export const getCurrentReferenceNode = createSelector(
getPropertyPaneState,
(pane: PropertyPaneReduxState) => {
return pane.widgetId && pane.node ? pane.node : undefined;
},
);
export const getPropertyConfig = createSelector(
getPropertyPaneConfig,
getPropertyPaneState,
@ -84,5 +77,5 @@ export const getIsPropertyPaneVisible = createSelector(
getPropertyPaneState,
getPropertyConfig,
(pane: PropertyPaneReduxState, content?: PropertySection[]) =>
!!(pane.isVisible && pane.widgetId && pane.node && content),
!!(pane.isVisible && pane.widgetId && content),
);

View File

@ -182,6 +182,7 @@ export const widgetOperationParams = (
widgetId: parentWidgetId,
payload: {
type: widget.type,
newWidgetId: generateReactKey(),
leftColumn,
topRow,
...widgetDimensions,
@ -239,7 +240,7 @@ export const generateWidgetProps = (
parentColumnSpace: number,
widgetName: string,
widgetConfig: Partial<WidgetProps>,
): ContainerWidgetProps<WidgetProps> => {
): Partial<ContainerWidgetProps<WidgetProps>> => {
if (parent && parent.snapColumns && parent.snapRows) {
const sizes = {
leftColumn,
@ -259,7 +260,6 @@ export const generateWidgetProps = (
return {
...widgetConfig,
type,
widgetId: generateReactKey(),
widgetName: widgetName,
isVisible: true,
isLoading: false,