diff --git a/packages/libs/wdk-client/src/Components/Overlays/Dialog.css b/packages/libs/wdk-client/src/Components/Overlays/Dialog.css index 740721cec9..b3d4116c5d 100644 --- a/packages/libs/wdk-client/src/Components/Overlays/Dialog.css +++ b/packages/libs/wdk-client/src/Components/Overlays/Dialog.css @@ -51,6 +51,9 @@ body.wdk-ModalOpen { border-top-right-radius: 4px; background: #eee; padding: 0.6em 0.8em; +} + +.wdk-DialogHeader.draggable { cursor: move; } diff --git a/packages/libs/wdk-client/src/Components/Overlays/Dialog.tsx b/packages/libs/wdk-client/src/Components/Overlays/Dialog.tsx index b8dbeba44c..3e6c7bafc1 100644 --- a/packages/libs/wdk-client/src/Components/Overlays/Dialog.tsx +++ b/packages/libs/wdk-client/src/Components/Overlays/Dialog.tsx @@ -1,4 +1,10 @@ -import React, { ReactNode, useEffect, useRef } from 'react'; +import React, { + CSSProperties, + ReactNode, + useEffect, + useRef, + useState, +} from 'react'; import Icon from '../../Components/Icon/Icon'; import { useBodyScrollManager } from '../../Components/Overlays/BodyScrollManager'; import Popup from '../../Components/Overlays/Popup'; @@ -20,10 +26,19 @@ type Props = { onClose?: () => void; }; +const resizeStyling: CSSProperties = { + resize: 'both', + overflow: 'auto', + minHeight: 100, +}; + function Dialog(props: Props) { const headerNode = useRef(null); useBodyScrollManager(props.open && !!props.modal); - useRestorePrevoiusFocus(props.open); + useRestorePreviousFocus(props.open); + const [isDragging, setIsDragging] = useState(false); + const [initialOffset, setInitialOffset] = + useState<{ left: number; top: number } | undefined>(undefined); if (!props.open) return null; @@ -35,16 +50,65 @@ function Dialog(props: Props) { , ], leftButtons, + draggable = true, } = props; + const handleDragStart = (e: React.MouseEvent) => { + setIsDragging(true); + if (headerNode.current && !initialOffset) { + const popupRect = + headerNode.current.parentElement?.getBoundingClientRect(); + setInitialOffset({ + left: e.clientX - (popupRect?.left ?? 0), + top: e.clientY - (popupRect?.top ?? 0), + }); + } + }; + + const handleDrag = (e: React.MouseEvent) => { + if (isDragging && headerNode.current && initialOffset) { + const popupContainer = headerNode.current.parentElement; + if (popupContainer) { + const popupRect = popupContainer.getBoundingClientRect(); + const left = + popupContainer.offsetLeft + + (e.clientX - popupRect.left) - + initialOffset.left; + const top = + popupContainer.offsetTop + + (e.clientY - popupRect.top) - + initialOffset.top; + popupContainer.style.left = left + 'px'; + popupContainer.style.top = top + 'px'; + } + } + }; + + const handleDragEnd = () => { + setIsDragging(false); + setInitialOffset(undefined); + }; + const content = (
{leftButtons} @@ -63,12 +127,7 @@ function Dialog(props: Props) { ); return ( - headerNode.current as HTMLDivElement} - open={props.open} - resizable={props.resizable} - > + {content} ); @@ -90,7 +149,7 @@ function makeClassName(className?: string, suffix = '', ...modifiers: any[]) { ); } -function useRestorePrevoiusFocus(isOpen: boolean) { +function useRestorePreviousFocus(isOpen: boolean) { const previousActiveRef = useRef(); useEffect(() => { if (isOpen) { diff --git a/packages/libs/wdk-client/src/Components/Overlays/Popup.tsx b/packages/libs/wdk-client/src/Components/Overlays/Popup.tsx index 2073e0aaa7..734f00892c 100644 --- a/packages/libs/wdk-client/src/Components/Overlays/Popup.tsx +++ b/packages/libs/wdk-client/src/Components/Overlays/Popup.tsx @@ -1,152 +1,22 @@ // Primitive component for creating a popup window -import $ from 'jquery'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import TabbableContainer from '../../Components/Display/TabbableContainer'; type Props = { - /** Should the popup be visible or not? */ - open: boolean; - - resizable?: boolean; - className?: string; - - /** - * Element which to append the draggable container. Defaults to - * `document.body`. - */ - parentSelector?: () => Element; - - /** - * Element to use to constrain dragging. If set, the popup can only be - * dragged within the returned Element. - */ - containerSelector?: () => Element; - - /** Should the popup be draggable? */ - draggable?: boolean; - - /** - * Set the element to use as a drag handle. This should be a descendent of the - * content root element. Only used if `draggable` is `true`. - */ - dragHandleSelector?: () => Element; - /** Content of popup */ children: React.ReactElement; }; -// TODO Replace jQueryUI plugin with react-dnd -/** - * Popup window - * - * @example - * ``` - * class App extends React.Component { - * render() { - * return ( - *
- * - * - *
- *

- * Some title - *
- * - *
- *

- *
Some content
- *
- *
- *
- * ); - * } - * } - * ``` - */ -class Popup extends React.Component { - static defaultProps = { - draggable: false, - }; - - containerNode?: HTMLElement; - - popupNode: Element | null = null; - - componentDidMount() { - // Create container node and attatch it to the parent node. - this.containerNode = document.createElement('div'); - const parent = - this.props.parentSelector == null - ? document.body - : this.props.parentSelector(); - if (parent !== this.containerNode.parentNode) { - parent.appendChild(this.containerNode); - } - - // Force this component to update, since the containerNode did not exist on - // the first render and we want to render the Portal now. This will also - // cause `componentDidUpdate` to be called. - this.forceUpdate(); - } - - componentDidUpdate() { - this._callJqueryWithProps(); - } - - componentWillUnmount() { - if (this.popupNode) $(this.popupNode).draggable('destroy'); - if (this.containerNode) this.containerNode.remove(); - } - - _callJqueryWithProps() { - if (this.popupNode == null) return; - const $node = $(this.popupNode) - .draggable({ - addClasses: false, - containment: - this.props.containerSelector == null - ? 'document' - : this.props.containerSelector(), - handle: - this.props.dragHandleSelector == null - ? false - : this.props.dragHandleSelector(), - }) - .toggle(this.props.open); - - if (this.props.resizable) { - $node.resizable({ - handles: 'all', - minWidth: 100, - minHeight: 100, - }); - } - } - - render() { - const children = React.cloneElement(this.props.children, { - ref: (c: React.ReactInstance | null) => - (this.popupNode = c && (ReactDOM.findDOMNode(c) as HTMLElement)), - }); - const content = ( - - {children} - - ); - return this.containerNode - ? ReactDOM.createPortal(content, this.containerNode) - : null; - } +function Popup({ className, children }: Props) { + const content = ( + + {children} + + ); + return ReactDOM.createPortal(content, document.body); } export default Popup;