import React, { useState, useLayoutEffect, MutableRefObject } from "react"; import styled from "styled-components"; import { WidgetProps, WidgetOperations } from "../widgets/BaseWidget"; import { useDrop } from "react-dnd"; import { ContainerProps } from "./ContainerComponent"; import WidgetFactory from "../utils/WidgetFactory"; import { snapToGrid } from "../utils/helpers"; import DragLayerComponent from "./DragLayerComponent"; import { GridDefaults } from "../constants/WidgetConstants"; const { DEFAULT_CELL_SIZE, DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH, } = GridDefaults; type DropTargetComponentProps = ContainerProps & { updateWidget?: Function; }; const WrappedDropTarget = styled.div` background: white; `; const DropTargetMask = styled.div` position: absolute; z-index: -10; left: 0; right: 0; `; export const DropTargetComponent = (props: DropTargetComponentProps) => { const [dropTargetTopLeft, setDropTargetTopLeft] = useState({ x: 0, y: 0 }); const dropTargetMask: MutableRefObject = React.useRef( null, ); useLayoutEffect(() => { const el = dropTargetMask.current; if (el) { const rect = el.getBoundingClientRect(); setDropTargetTopLeft({ x: rect.left, y: rect.top, }); } }, [setDropTargetTopLeft]); const [{ isOver }, drop] = useDrop({ accept: Object.values(WidgetFactory.getWidgetTypes()), drop(item: WidgetProps, monitor) { if (monitor.isOver({ shallow: true })) { const item = monitor.getItem(); const clientOffset = monitor.getClientOffset(); if (clientOffset) { const [x, y] = snapToGrid( DEFAULT_CELL_SIZE, clientOffset.x - dropTargetTopLeft.x, clientOffset.y - dropTargetTopLeft.y, ); if (item.widgetId) { props.updateWidget && props.updateWidget(WidgetOperations.MOVE, item.widgetId, { left: x, top: y, }); } else { props.updateWidget && props.updateWidget(WidgetOperations.ADD_CHILD, props.widgetId, { type: item.type, left: x, top: y, width: Math.round(DEFAULT_WIDGET_WIDTH / DEFAULT_CELL_SIZE) * DEFAULT_CELL_SIZE, height: Math.round(DEFAULT_WIDGET_HEIGHT / DEFAULT_CELL_SIZE) * DEFAULT_CELL_SIZE, }); } } } return undefined; }, collect: monitor => ({ isOver: !!monitor.isOver({ shallow: true }), }), canDrop: (item, monitor) => { return monitor.isOver({ shallow: true }); }, }); return ( {props.children} ); }; export default DropTargetComponent;