2020-01-16 11:46:21 +00:00
|
|
|
import React, {
|
|
|
|
|
ReactNode,
|
|
|
|
|
Context,
|
|
|
|
|
createContext,
|
2021-08-12 05:45:38 +00:00
|
|
|
memo,
|
2020-01-16 11:46:21 +00:00
|
|
|
useEffect,
|
2021-08-12 05:45:38 +00:00
|
|
|
useRef,
|
2021-07-23 06:40:39 +00:00
|
|
|
useCallback,
|
|
|
|
|
useMemo,
|
2020-01-16 11:46:21 +00:00
|
|
|
} from "react";
|
|
|
|
|
import styled from "styled-components";
|
2021-07-23 06:40:39 +00:00
|
|
|
import { isEqual } from "lodash";
|
2019-11-13 07:00:25 +00:00
|
|
|
import { WidgetProps } from "widgets/BaseWidget";
|
2021-08-12 05:45:38 +00:00
|
|
|
import { getCanvasSnapRows } from "utils/WidgetPropsUtils";
|
2020-01-16 11:46:21 +00:00
|
|
|
import {
|
|
|
|
|
MAIN_CONTAINER_WIDGET_ID,
|
2020-03-27 09:02:11 +00:00
|
|
|
GridDefaults,
|
2020-01-16 11:46:21 +00:00
|
|
|
} from "constants/WidgetConstants";
|
|
|
|
|
import { calculateDropTargetRows } from "./DropTargetUtils";
|
2019-09-25 17:24:23 +00:00
|
|
|
import DragLayerComponent from "./DragLayerComponent";
|
2020-01-16 11:46:21 +00:00
|
|
|
import { AppState } from "reducers";
|
|
|
|
|
import { useSelector } from "react-redux";
|
2020-01-20 09:00:37 +00:00
|
|
|
import {
|
|
|
|
|
useShowPropertyPane,
|
2020-03-27 09:02:11 +00:00
|
|
|
useCanvasSnapRowsUpdateHook,
|
2020-01-20 09:00:37 +00:00
|
|
|
} from "utils/hooks/dragResizeHooks";
|
2021-07-23 06:40:39 +00:00
|
|
|
import { getOccupiedSpacesSelectorForContainer } from "selectors/editorSelectors";
|
2021-06-17 13:26:54 +00:00
|
|
|
import { useWidgetSelection } from "utils/hooks/useWidgetSelection";
|
2021-08-12 05:45:38 +00:00
|
|
|
import { getDragDetails } from "sagas/selectors";
|
2019-09-22 20:25:05 +00:00
|
|
|
|
2019-11-13 07:00:25 +00:00
|
|
|
type DropTargetComponentProps = WidgetProps & {
|
|
|
|
|
children?: ReactNode;
|
2019-09-25 17:24:23 +00:00
|
|
|
snapColumnSpace: number;
|
|
|
|
|
snapRowSpace: number;
|
2020-03-27 09:02:11 +00:00
|
|
|
minHeight: number;
|
2021-04-23 05:43:13 +00:00
|
|
|
noPad?: boolean;
|
2019-09-17 15:09:55 +00:00
|
|
|
};
|
2019-09-19 22:25:37 +00:00
|
|
|
|
2020-01-16 11:46:21 +00:00
|
|
|
const StyledDropTarget = styled.div`
|
|
|
|
|
transition: height 100ms ease-in;
|
2020-03-27 09:02:11 +00:00
|
|
|
width: 100%;
|
|
|
|
|
position: relative;
|
|
|
|
|
background: none;
|
|
|
|
|
user-select: none;
|
2021-08-12 05:45:38 +00:00
|
|
|
z-index: 1;
|
2020-01-16 11:46:21 +00:00
|
|
|
`;
|
|
|
|
|
|
2021-04-28 10:28:39 +00:00
|
|
|
function Onboarding() {
|
2020-05-14 06:06:20 +00:00
|
|
|
return (
|
2021-11-23 08:01:46 +00:00
|
|
|
<h2 className="absolute top-0 left-0 right-0 flex items-end h-108 justify-center text-2xl font-bold text-gray-300">
|
|
|
|
|
Drag and drop a widget here
|
|
|
|
|
</h2>
|
2020-05-14 06:06:20 +00:00
|
|
|
);
|
2021-04-28 10:28:39 +00:00
|
|
|
}
|
2020-05-14 06:06:20 +00:00
|
|
|
|
2020-03-06 09:45:21 +00:00
|
|
|
/*
|
2020-01-16 11:46:21 +00:00
|
|
|
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<{
|
2021-08-12 05:45:38 +00:00
|
|
|
updateDropTargetRows?: (
|
2022-01-25 15:28:31 +00:00
|
|
|
widgetIdsToExclude: string[],
|
2021-08-12 05:45:38 +00:00
|
|
|
widgetBottomRow: number,
|
|
|
|
|
) => number | false;
|
2020-01-16 11:46:21 +00:00
|
|
|
}> = createContext({});
|
|
|
|
|
|
2021-04-28 10:28:39 +00:00
|
|
|
export function DropTargetComponent(props: DropTargetComponentProps) {
|
2020-03-27 09:02:11 +00:00
|
|
|
const canDropTargetExtend = props.canExtend;
|
|
|
|
|
const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend);
|
|
|
|
|
|
2020-01-20 09:00:37 +00:00
|
|
|
const isResizing = useSelector(
|
|
|
|
|
(state: AppState) => state.ui.widgetDragResize.isResizing,
|
2020-01-16 11:46:21 +00:00
|
|
|
);
|
2020-02-18 19:56:58 +00:00
|
|
|
const isDragging = useSelector(
|
|
|
|
|
(state: AppState) => state.ui.widgetDragResize.isDragging,
|
|
|
|
|
);
|
2020-01-16 11:46:21 +00:00
|
|
|
|
2021-08-12 05:45:38 +00:00
|
|
|
// dragDetails contains of info needed for a container jump:
|
|
|
|
|
// which parent the dragging widget belongs,
|
|
|
|
|
// which canvas is active(being dragged on),
|
|
|
|
|
// which widget is grabbed while dragging started,
|
|
|
|
|
// relative position of mouse pointer wrt to the last grabbed widget.
|
|
|
|
|
const dragDetails = useSelector(getDragDetails);
|
|
|
|
|
|
|
|
|
|
const { draggedOn } = dragDetails;
|
|
|
|
|
|
2021-08-02 11:16:50 +00:00
|
|
|
const childWidgets: string[] | undefined = useSelector(
|
|
|
|
|
(state: AppState) => state.entities.canvasWidgets[props.widgetId]?.children,
|
2020-01-16 11:46:21 +00:00
|
|
|
);
|
|
|
|
|
|
2021-07-23 06:40:39 +00:00
|
|
|
const selectOccupiedSpaces = useCallback(
|
|
|
|
|
getOccupiedSpacesSelectorForContainer(props.widgetId),
|
|
|
|
|
[props.widgetId],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const occupiedSpacesByChildren = useSelector(selectOccupiedSpaces, isEqual);
|
2020-01-16 11:46:21 +00:00
|
|
|
|
2021-08-12 05:45:38 +00:00
|
|
|
const rowRef = useRef(snapRows);
|
2020-01-16 11:46:21 +00:00
|
|
|
|
2020-03-27 09:02:11 +00:00
|
|
|
const showPropertyPane = useShowPropertyPane();
|
2021-08-12 05:45:38 +00:00
|
|
|
const { deselectAll, focusWidget } = useWidgetSelection();
|
2020-03-27 09:02:11 +00:00
|
|
|
const updateCanvasSnapRows = useCanvasSnapRowsUpdateHook();
|
2022-01-13 13:21:57 +00:00
|
|
|
const showDragLayer =
|
|
|
|
|
(isDragging && draggedOn === props.widgetId) || isResizing;
|
2020-03-27 09:02:11 +00:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend);
|
2021-08-12 05:45:38 +00:00
|
|
|
if (rowRef.current !== snapRows) {
|
|
|
|
|
rowRef.current = snapRows;
|
|
|
|
|
updateHeight();
|
|
|
|
|
if (canDropTargetExtend) {
|
|
|
|
|
updateCanvasSnapRows(props.widgetId, snapRows);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-27 09:02:11 +00:00
|
|
|
}, [props.bottomRow, props.canExtend]);
|
2022-01-13 13:21:57 +00:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (!isDragging || !isResizing) {
|
|
|
|
|
// bottom row of canvas can increase by any number as user moves/resizes any widget towards the bottom of the canvas
|
|
|
|
|
// but canvas height is not lost when user moves/resizes back top.
|
|
|
|
|
// it is done that way to have a pleasant building experience.
|
|
|
|
|
// post drop the bottom most row is used to appropriately calculate the canvas height and lose unwanted height.
|
|
|
|
|
rowRef.current = snapRows;
|
|
|
|
|
updateHeight();
|
|
|
|
|
}
|
|
|
|
|
}, [isDragging, isResizing]);
|
2020-03-27 09:02:11 +00:00
|
|
|
|
2021-08-12 05:45:38 +00:00
|
|
|
const updateHeight = () => {
|
|
|
|
|
if (dropTargetRef.current) {
|
|
|
|
|
const height = canDropTargetExtend
|
|
|
|
|
? `${Math.max(rowRef.current * props.snapRowSpace, props.minHeight)}px`
|
|
|
|
|
: "100%";
|
|
|
|
|
dropTargetRef.current.style.height = height;
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-01-25 15:28:31 +00:00
|
|
|
const updateDropTargetRows = (
|
|
|
|
|
widgetIdsToExclude: string[],
|
|
|
|
|
widgetBottomRow: number,
|
|
|
|
|
) => {
|
2021-08-12 05:45:38 +00:00
|
|
|
if (canDropTargetExtend) {
|
2020-03-27 09:02:11 +00:00
|
|
|
const newRows = calculateDropTargetRows(
|
2022-01-25 15:28:31 +00:00
|
|
|
widgetIdsToExclude,
|
2020-03-27 09:02:11 +00:00
|
|
|
widgetBottomRow,
|
|
|
|
|
props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1,
|
|
|
|
|
occupiedSpacesByChildren,
|
|
|
|
|
);
|
2021-08-12 05:45:38 +00:00
|
|
|
if (rowRef.current < newRows) {
|
|
|
|
|
rowRef.current = newRows;
|
|
|
|
|
updateHeight();
|
|
|
|
|
return newRows;
|
2020-01-16 11:46:21 +00:00
|
|
|
}
|
|
|
|
|
return false;
|
2019-09-25 17:24:23 +00:00
|
|
|
}
|
2021-08-12 05:45:38 +00:00
|
|
|
return false;
|
2019-09-25 17:24:23 +00:00
|
|
|
};
|
|
|
|
|
|
2020-09-16 10:28:01 +00:00
|
|
|
const handleFocus = (e: any) => {
|
|
|
|
|
if (!isResizing && !isDragging) {
|
|
|
|
|
if (!props.parentId) {
|
2021-05-18 18:29:39 +00:00
|
|
|
deselectAll();
|
2020-09-16 10:28:01 +00:00
|
|
|
focusWidget && focusWidget(props.widgetId);
|
2021-03-29 15:47:22 +00:00
|
|
|
showPropertyPane && showPropertyPane();
|
2020-09-16 10:28:01 +00:00
|
|
|
}
|
2019-10-08 10:16:07 +00:00
|
|
|
}
|
2021-04-23 05:43:13 +00:00
|
|
|
// commenting this out to allow propagation of click events
|
|
|
|
|
// e.stopPropagation();
|
2020-09-16 10:28:01 +00:00
|
|
|
e.preventDefault();
|
2019-10-08 10:16:07 +00:00
|
|
|
};
|
2020-03-27 09:02:11 +00:00
|
|
|
const height = canDropTargetExtend
|
2021-08-12 05:45:38 +00:00
|
|
|
? `${Math.max(rowRef.current * props.snapRowSpace, props.minHeight)}px`
|
2020-03-27 09:02:11 +00:00
|
|
|
: "100%";
|
2021-06-17 13:26:54 +00:00
|
|
|
const boxShadow =
|
2021-08-12 05:45:38 +00:00
|
|
|
(isResizing || isDragging) && props.widgetId === MAIN_CONTAINER_WIDGET_ID
|
2021-11-23 08:01:46 +00:00
|
|
|
? "inset 0px 0px 0px 1px #DDDDDD"
|
2021-06-17 13:26:54 +00:00
|
|
|
: "0px 0px 0px 1px transparent";
|
2021-08-12 05:45:38 +00:00
|
|
|
const dropTargetRef = useRef<HTMLDivElement>(null);
|
2021-04-23 05:43:13 +00:00
|
|
|
|
2021-07-23 06:40:39 +00:00
|
|
|
// memoizing context values
|
|
|
|
|
const contextValue = useMemo(() => {
|
|
|
|
|
return {
|
|
|
|
|
updateDropTargetRows,
|
|
|
|
|
};
|
2021-09-21 07:55:56 +00:00
|
|
|
}, [updateDropTargetRows, occupiedSpacesByChildren]);
|
2019-09-17 10:09:00 +00:00
|
|
|
return (
|
2021-07-23 06:40:39 +00:00
|
|
|
<DropTargetContext.Provider value={contextValue}>
|
2020-01-16 11:46:21 +00:00
|
|
|
<StyledDropTarget
|
2021-09-09 15:10:22 +00:00
|
|
|
className="t--drop-target"
|
2020-01-16 11:46:21 +00:00
|
|
|
onClick={handleFocus}
|
2021-08-12 05:45:38 +00:00
|
|
|
ref={dropTargetRef}
|
2020-01-16 11:46:21 +00:00
|
|
|
style={{
|
|
|
|
|
height,
|
2021-06-17 13:26:54 +00:00
|
|
|
boxShadow,
|
2020-01-16 11:46:21 +00:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{props.children}
|
2020-05-14 06:06:20 +00:00
|
|
|
{!(childWidgets && childWidgets.length) &&
|
|
|
|
|
!isDragging &&
|
|
|
|
|
!props.parentId && <Onboarding />}
|
2022-01-13 13:21:57 +00:00
|
|
|
{showDragLayer && (
|
2021-08-12 05:45:38 +00:00
|
|
|
<DragLayerComponent
|
|
|
|
|
noPad={props.noPad || false}
|
|
|
|
|
parentColumnWidth={props.snapColumnSpace}
|
|
|
|
|
parentRowHeight={props.snapRowSpace}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2020-01-16 11:46:21 +00:00
|
|
|
</StyledDropTarget>
|
|
|
|
|
</DropTargetContext.Provider>
|
2019-09-17 10:09:00 +00:00
|
|
|
);
|
2021-04-28 10:28:39 +00:00
|
|
|
}
|
2021-04-23 05:43:13 +00:00
|
|
|
|
|
|
|
|
const MemoizedDropTargetComponent = memo(DropTargetComponent);
|
2019-09-17 10:09:00 +00:00
|
|
|
|
2021-04-23 05:43:13 +00:00
|
|
|
export default MemoizedDropTargetComponent;
|