2019-12-25 09:17:37 +00:00
|
|
|
import React, { useState, useContext, ReactNode } from "react";
|
2019-11-13 07:00:25 +00:00
|
|
|
import { useDrop, XYCoord, DropTargetMonitor } from "react-dnd";
|
|
|
|
|
import { WidgetProps } from "widgets/BaseWidget";
|
|
|
|
|
import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer";
|
|
|
|
|
import WidgetFactory from "utils/WidgetFactory";
|
|
|
|
|
import { widgetOperationParams, noCollision } from "utils/WidgetPropsUtils";
|
|
|
|
|
import { EditorContext } from "components/editorComponents/EditorContextProvider";
|
2019-12-27 10:10:04 +00:00
|
|
|
import { FocusContext, ResizingContext } from "pages/Editor/CanvasContexts";
|
2019-11-13 07:00:25 +00:00
|
|
|
|
2019-09-25 17:24:23 +00:00
|
|
|
import DragLayerComponent from "./DragLayerComponent";
|
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;
|
2019-09-17 15:09:55 +00:00
|
|
|
};
|
2019-09-19 22:25:37 +00:00
|
|
|
|
2019-09-25 17:24:23 +00:00
|
|
|
type DropTargetBounds = {
|
|
|
|
|
x: number;
|
|
|
|
|
y: number;
|
|
|
|
|
width: number;
|
|
|
|
|
height: number;
|
|
|
|
|
};
|
2019-09-19 22:25:37 +00:00
|
|
|
|
2019-09-17 10:09:00 +00:00
|
|
|
export const DropTargetComponent = (props: DropTargetComponentProps) => {
|
2019-09-25 21:21:04 +00:00
|
|
|
// Hook to keep the offset of the drop target container in state
|
2019-09-25 17:24:23 +00:00
|
|
|
const [dropTargetOffset, setDropTargetOffset] = useState({ x: 0, y: 0 });
|
2019-11-13 07:00:25 +00:00
|
|
|
const { updateWidget, occupiedSpaces } = useContext(EditorContext);
|
2020-01-06 11:02:22 +00:00
|
|
|
const { selectWidget, showPropertyPane } = useContext(FocusContext);
|
2019-12-25 09:17:37 +00:00
|
|
|
const { isResizing } = useContext(ResizingContext);
|
2019-11-13 07:00:25 +00:00
|
|
|
const spacesOccupiedBySiblingWidgets =
|
|
|
|
|
occupiedSpaces && occupiedSpaces[props.widgetId]
|
|
|
|
|
? occupiedSpaces[props.widgetId]
|
|
|
|
|
: undefined;
|
2019-09-25 17:24:23 +00:00
|
|
|
// Make this component a drop target
|
2019-10-03 15:14:50 +00:00
|
|
|
const [{ isOver, isExactlyOver }, drop] = useDrop({
|
2019-09-17 10:09:00 +00:00
|
|
|
accept: Object.values(WidgetFactory.getWidgetTypes()),
|
2019-09-25 17:24:23 +00:00
|
|
|
drop(widget: WidgetProps & Partial<WidgetConfigProps>, monitor) {
|
|
|
|
|
// Make sure we're dropping in this container.
|
2019-11-13 07:00:25 +00:00
|
|
|
if (isExactlyOver) {
|
|
|
|
|
const updateWidgetParams = widgetOperationParams(
|
|
|
|
|
widget,
|
2019-12-27 10:10:04 +00:00
|
|
|
monitor.getSourceClientOffset() as XYCoord,
|
2019-11-13 07:00:25 +00:00
|
|
|
dropTargetOffset,
|
|
|
|
|
props.snapColumnSpace,
|
|
|
|
|
props.snapRowSpace,
|
|
|
|
|
props.widgetId,
|
|
|
|
|
);
|
2020-01-07 12:28:58 +00:00
|
|
|
// Only show propertypane if this is a new widget.
|
|
|
|
|
// If it is not a new widget, then let the DraggableComponent handle it.
|
2020-01-02 12:42:02 +00:00
|
|
|
showPropertyPane &&
|
2020-01-07 12:28:58 +00:00
|
|
|
updateWidgetParams.payload.newWidgetId &&
|
2020-01-02 12:42:02 +00:00
|
|
|
showPropertyPane(updateWidgetParams.payload.newWidgetId);
|
|
|
|
|
|
2019-10-03 16:24:29 +00:00
|
|
|
updateWidget &&
|
|
|
|
|
updateWidget(
|
2019-11-13 07:00:25 +00:00
|
|
|
updateWidgetParams.operation,
|
|
|
|
|
updateWidgetParams.widgetId,
|
|
|
|
|
updateWidgetParams.payload,
|
2019-09-19 22:25:37 +00:00
|
|
|
);
|
2019-09-17 10:09:00 +00:00
|
|
|
}
|
|
|
|
|
return undefined;
|
|
|
|
|
},
|
2019-09-25 17:24:23 +00:00
|
|
|
// Collect isOver for ui transforms when hovering over this component
|
2019-11-13 07:00:25 +00:00
|
|
|
collect: (monitor: DropTargetMonitor) => ({
|
2019-10-02 21:14:58 +00:00
|
|
|
isOver:
|
|
|
|
|
(monitor.isOver({ shallow: true }) &&
|
|
|
|
|
props.widgetId !== monitor.getItem().widgetId) ||
|
|
|
|
|
(monitor.isOver() && props.widgetId !== monitor.getItem().widgetId),
|
2019-10-03 15:14:50 +00:00
|
|
|
isExactlyOver: monitor.isOver({ shallow: true }),
|
2019-11-13 07:00:25 +00:00
|
|
|
draggingItem: monitor.getItem() as WidgetProps,
|
2019-09-19 22:25:37 +00:00
|
|
|
}),
|
2019-09-25 17:24:23 +00:00
|
|
|
// Only allow drop if the drag object is directly over this component
|
|
|
|
|
// As opposed to the drag object being over a child component, or outside the component bounds
|
2019-09-25 21:21:04 +00:00
|
|
|
// Also only if the dropzone does not overlap any existing children
|
2019-09-24 12:36:03 +00:00
|
|
|
canDrop: (widget, monitor) => {
|
2019-10-02 21:14:58 +00:00
|
|
|
// Check if the draggable is the same as the dropTarget
|
2019-11-13 07:00:25 +00:00
|
|
|
if (isExactlyOver) {
|
|
|
|
|
const hasCollision = !noCollision(
|
2019-12-27 10:10:04 +00:00
|
|
|
monitor.getSourceClientOffset() as XYCoord,
|
2019-09-25 21:21:04 +00:00
|
|
|
props.snapColumnSpace,
|
|
|
|
|
props.snapRowSpace,
|
|
|
|
|
widget,
|
|
|
|
|
dropTargetOffset,
|
2019-11-13 07:00:25 +00:00
|
|
|
spacesOccupiedBySiblingWidgets,
|
2019-10-08 06:19:10 +00:00
|
|
|
props.snapRows,
|
|
|
|
|
props.snapColumns,
|
2019-09-25 21:21:04 +00:00
|
|
|
);
|
2019-11-13 07:00:25 +00:00
|
|
|
return !hasCollision;
|
2019-09-25 21:21:04 +00:00
|
|
|
}
|
|
|
|
|
return false;
|
2019-09-17 10:09:00 +00:00
|
|
|
},
|
|
|
|
|
});
|
2019-09-25 17:24:23 +00:00
|
|
|
|
|
|
|
|
const handleBoundsUpdate = (rect: DOMRect) => {
|
|
|
|
|
if (rect.x !== dropTargetOffset.x || rect.y !== dropTargetOffset.y) {
|
|
|
|
|
setDropTargetOffset({
|
|
|
|
|
x: rect.x,
|
|
|
|
|
y: rect.y,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-08 10:16:07 +00:00
|
|
|
const handleFocus = () => {
|
2020-01-07 12:28:58 +00:00
|
|
|
if (!props.parentId && !isResizing) {
|
2020-01-06 11:02:22 +00:00
|
|
|
selectWidget && selectWidget(props.widgetId);
|
Property Pane Controls
- Fixes #121, #122, #123, #124, #90, #46, #65, #100, #101, #68, #102
2019-10-24 05:24:45 +00:00
|
|
|
showPropertyPane && showPropertyPane();
|
2019-10-08 10:16:07 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-09-17 10:09:00 +00:00
|
|
|
return (
|
2019-12-25 09:17:37 +00:00
|
|
|
<div
|
|
|
|
|
onClick={handleFocus}
|
|
|
|
|
ref={drop}
|
|
|
|
|
style={{
|
|
|
|
|
position: "relative",
|
|
|
|
|
left: 0,
|
|
|
|
|
height: "100%",
|
|
|
|
|
width: "100%",
|
|
|
|
|
top: 0,
|
|
|
|
|
userSelect: "none",
|
|
|
|
|
opacity: 0.99,
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{props.children}
|
|
|
|
|
<DragLayerComponent
|
|
|
|
|
parentOffset={dropTargetOffset}
|
|
|
|
|
parentRowHeight={props.snapRowSpace}
|
|
|
|
|
parentColumnWidth={props.snapColumnSpace}
|
|
|
|
|
visible={isOver || !!isResizing}
|
|
|
|
|
isOver={isExactlyOver}
|
|
|
|
|
dropTargetOffset={dropTargetOffset}
|
|
|
|
|
occupiedSpaces={spacesOccupiedBySiblingWidgets}
|
|
|
|
|
onBoundsUpdate={handleBoundsUpdate}
|
|
|
|
|
parentRows={props.snapRows}
|
|
|
|
|
parentCols={props.snapColumns}
|
|
|
|
|
isResizing={isResizing}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2019-09-17 10:09:00 +00:00
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default DropTargetComponent;
|