diff --git a/components/lib/dialog/Dialog.js b/components/lib/dialog/Dialog.js
index 1356dbda48..6b55ba9845 100644
--- a/components/lib/dialog/Dialog.js
+++ b/components/lib/dialog/Dialog.js
@@ -1,7 +1,7 @@
import * as React from 'react';
import PrimeReact, { localeOption } from '../api/Api';
import { CSSTransition } from '../csstransition/CSSTransition';
-import { useDraggable, useEventListener, useMountEffect, useUnmountEffect, useUpdateEffect } from '../hooks/Hooks';
+import { useEventListener, useMountEffect, useUnmountEffect, useUpdateEffect } from '../hooks/Hooks';
import { Portal } from '../portal/Portal';
import { Ripple } from '../ripple/Ripple';
import { classNames, DomHandler, ObjectUtils, UniqueComponentId, ZIndexUtils } from '../utils/Utils';
@@ -11,13 +11,13 @@ export const Dialog = React.forwardRef((props, ref) => {
const [maskVisibleState, setMaskVisibleState] = React.useState(false);
const [visibleState, setVisibleState] = React.useState(false);
const [maximizedState, setMaximizedState] = React.useState(props.maximized);
- const [draggableState, setDraggableState] = React.useState(false);
const dialogRef = React.useRef(null);
- const headerRef = React.useRef(null);
const maskRef = React.useRef(null);
const contentRef = React.useRef(null);
+ const headerRef = React.useRef(null);
const footerRef = React.useRef(null);
const closeRef = React.useRef(null);
+ const dragging = React.useRef(false);
const resizing = React.useRef(false);
const lastPageX = React.useRef(null);
const lastPageY = React.useRef(null);
@@ -25,10 +25,11 @@ export const Dialog = React.forwardRef((props, ref) => {
const attributeSelector = React.useRef('');
const maximized = props.onMaximize ? props.maximized : maximizedState;
- const draggable = useDraggable({ targetRef: dialogRef, handleRef: headerRef, onDragStart: props.onDragStart, onDrag: props.onDrag, onDragEnd: props.onDragEnd, enabled: draggableState, keepInViewport: props.keepInViewport });
const [bindDocumentKeyDownListener, unbindDocumentKeyDownListener] = useEventListener({ type: 'keydown', listener: (event) => onKeyDown(event) });
const [bindDocumentResizeListener, unbindDocumentResizeListener] = useEventListener({ type: 'mousemove', target: () => window.document, listener: (event) => onResize(event) });
const [bindDocumentResizeEndListener, unbindDocumentResizEndListener] = useEventListener({ type: 'mouseup', target: () => window.document, listener: (event) => onResizeEnd(event) });
+ const [bindDocumentDragListener, unbindDocumentDragListener] = useEventListener({ type: 'mousemove', target: () => window.document, listener: (event) => onDrag(event) });
+ const [bindDocumentDragEndListener, unbindDocumentDragEndListener] = useEventListener({ type: 'mouseup', target: () => window.document, listener: (event) => onDragEnd(event) });
const onClose = (event) => {
props.onHide();
@@ -108,6 +109,65 @@ export const Dialog = React.forwardRef((props, ref) => {
}
};
+ const onDragStart = (event) => {
+ if (DomHandler.hasClass(event.target, 'p-dialog-header-icon') || DomHandler.hasClass(event.target.parentElement, 'p-dialog-header-icon')) {
+ return;
+ }
+
+ if (props.draggable) {
+ dragging.current = true;
+ lastPageX.current = event.pageX;
+ lastPageY.current = event.pageY;
+ dialogRef.current.style.margin = '0';
+ DomHandler.addClass(document.body, 'p-unselectable-text');
+
+ props.onDragStart && props.onDragStart(event);
+ }
+ };
+
+ const onDrag = (event) => {
+ if (dragging.current) {
+ const width = DomHandler.getOuterWidth(dialogRef.current);
+ const height = DomHandler.getOuterHeight(dialogRef.current);
+ const deltaX = event.pageX - lastPageX.current;
+ const deltaY = event.pageY - lastPageY.current;
+ const offset = dialogRef.current.getBoundingClientRect();
+ const leftPos = offset.left + deltaX;
+ const topPos = offset.top + deltaY;
+ const viewport = DomHandler.getViewport();
+
+ dialogRef.current.style.position = 'fixed';
+
+ if (props.keepInViewport) {
+ if (leftPos >= props.minX && leftPos + width < viewport.width) {
+ lastPageX.current = event.pageX;
+ dialogRef.current.style.left = leftPos + 'px';
+ }
+
+ if (topPos >= props.minY && topPos + height < viewport.height) {
+ lastPageY.current = event.pageY;
+ dialogRef.current.style.top = topPos + 'px';
+ }
+ } else {
+ lastPageX.current = event.pageX;
+ dialogRef.current.style.left = leftPos + 'px';
+ lastPageY.current = event.pageY;
+ dialogRef.current.style.top = topPos + 'px';
+ }
+
+ props.onDrag && props.onDrag(event);
+ }
+ };
+
+ const onDragEnd = (event) => {
+ if (dragging.current) {
+ dragging.current = false;
+ DomHandler.removeClass(document.body, 'p-unselectable-text');
+
+ props.onDragEnd && props.onDragEnd(event);
+ }
+ };
+
const onResizeStart = (event) => {
if (props.resizable) {
resizing.current = true;
@@ -191,7 +251,6 @@ export const Dialog = React.forwardRef((props, ref) => {
const onEnter = () => {
dialogRef.current.setAttribute(attributeSelector.current, '');
- setDraggableState(props.draggable);
};
const onEntered = () => {
@@ -215,7 +274,7 @@ export const Dialog = React.forwardRef((props, ref) => {
};
const onExited = () => {
- setDraggableState(false);
+ dragging.current = false;
ZIndexUtils.clear(maskRef.current);
setMaskVisibleState(false);
disableDocumentSettings();
@@ -246,6 +305,11 @@ export const Dialog = React.forwardRef((props, ref) => {
};
const bindGlobalListeners = () => {
+ if (props.draggable) {
+ bindDocumentDragListener();
+ bindDocumentDragEndListener();
+ }
+
if (props.resizable) {
bindDocumentResizeListener();
bindDocumentResizeEndListener();
@@ -258,6 +322,8 @@ export const Dialog = React.forwardRef((props, ref) => {
};
const unbindGlobalListeners = () => {
+ unbindDocumentDragListener();
+ unbindDocumentDragEndListener();
unbindDocumentResizeListener();
unbindDocumentResizEndListener();
unbindDocumentKeyDownListener();
@@ -394,7 +460,7 @@ export const Dialog = React.forwardRef((props, ref) => {
const headerClassName = classNames('p-dialog-header', props.headerClassName);
return (
-
+
diff --git a/components/lib/hooks/Hooks.js b/components/lib/hooks/Hooks.js
index 35e84d63bb..0327f1710e 100644
--- a/components/lib/hooks/Hooks.js
+++ b/components/lib/hooks/Hooks.js
@@ -1,14 +1,13 @@
-import { useDraggable } from './useDraggable';
-import { useEventListener } from './useEventListener';
-import { useInterval } from './useInterval';
+import { usePrevious } from './usePrevious';
import { useMountEffect } from './useMountEffect';
+import { useUpdateEffect } from './useUpdateEffect';
+import { useUnmountEffect } from './useUnmountEffect';
+import { useEventListener } from './useEventListener';
import { useOverlayListener } from './useOverlayListener';
import { useOverlayScrollListener } from './useOverlayScrollListener';
-import { usePrevious } from './usePrevious';
import { useResizeListener } from './useResizeListener';
-import { useLocalStorage, useSessionStorage, useStorage } from './useStorage';
+import { useInterval } from './useInterval';
+import { useStorage, useLocalStorage, useSessionStorage } from './useStorage';
import { useTimeout } from './useTimeout';
-import { useUnmountEffect } from './useUnmountEffect';
-import { useUpdateEffect } from './useUpdateEffect';
-export { usePrevious, useMountEffect, useUpdateEffect, useUnmountEffect, useEventListener, useOverlayListener, useOverlayScrollListener, useResizeListener, useInterval, useStorage, useLocalStorage, useSessionStorage, useTimeout, useDraggable };
+export { usePrevious, useMountEffect, useUpdateEffect, useUnmountEffect, useEventListener, useOverlayListener, useOverlayScrollListener, useResizeListener, useInterval, useStorage, useLocalStorage, useSessionStorage, useTimeout };
diff --git a/components/lib/hooks/hooks.d.ts b/components/lib/hooks/hooks.d.ts
index 2a37b97028..01aa810bf9 100644
--- a/components/lib/hooks/hooks.d.ts
+++ b/components/lib/hooks/hooks.d.ts
@@ -22,18 +22,6 @@ interface ResizeEventOptions {
listener?(event: Event): void;
}
-interface DraggableOptions {
- targetRef: React.Ref;
- handleRef: React.Ref;
- onDrag?(e: React.DragEvent): void;
- onDragEnd?(e: React.DragEvent): void;
- onDragStart?(e: React.DragEvent): void;
- enabled: true;
- keepInViewport: false;
- rectLimits?: DOMRect;
-}
-
-export declare function useDraggable(options: DraggableOptions): any;
export declare function usePrevious(value: any): any;
export declare function useMountEffect(effect: React.EffectCallback): void;
export declare function useUpdateEffect(effect: React.EffectCallback, deps?: React.DependencyList): void;
diff --git a/components/lib/hooks/useDraggable.js b/components/lib/hooks/useDraggable.js
deleted file mode 100644
index 749d672a62..0000000000
--- a/components/lib/hooks/useDraggable.js
+++ /dev/null
@@ -1,171 +0,0 @@
-import { useCallback, useEffect, useRef, useState } from 'react';
-import { DomHandler } from '../utils/Utils';
-
-/**
- * Hook to wrap up draggable logic for dialogs.
- *
- * @param targetRef the target ref of the draggable
- * @param handleRef the handle ref of the draggable
- * @param onDragStart callback
- * @param onDrag callback
- * @param onDragEnd callback
- * @param enabled boolean whether this hook is active or not
- * @param keepInViewport should the draggable be contained by the viewport
- * @param rectLimits a bounding box to limit the draggable to
- * @returns { dragging, delta, resetState }
- */
-export const useDraggable = ({ targetRef, handleRef, onDragStart, onDragEnd, onDrag, enabled = true, keepInViewport = false, rectLimits }) => {
- const [dragging, setDragging] = useState(false);
- const [previous, setPrevious] = useState({ x: 0, y: 0 });
- const [delta, setDelta] = useState({ x: 0, y: 0 });
- const initial = useRef({ x: 0, y: 0 });
- const limits = useRef(null);
-
- /**
- * Subscribe to mouse/touch events to start dragging.
- */
- useEffect(() => {
- const handle = handleRef.current || targetRef.current;
-
- if (!handle || !enabled) {
- return;
- }
-
- handle.addEventListener('mousedown', startDragging);
- handle.addEventListener('touchstart', startDragging);
-
- return () => {
- handle.removeEventListener('mousedown', startDragging);
- handle.removeEventListener('touchstart', startDragging);
- };
-
- function startDragging(event) {
- setDragging(true);
- event.preventDefault();
- targetRef.current.style.willChange = 'transform';
- const source = (event.touches && event.touches[0]) || event;
-
- initial.current = { x: source.clientX, y: source.clientY };
-
- if (keepInViewport || rectLimits) {
- const { left, top, width, height } = targetRef.current.getBoundingClientRect();
- const viewport = DomHandler.getViewport();
-
- if (keepInViewport) {
- limits.current = {
- minX: -left + delta.x,
- maxX: viewport.width - width - left + delta.x,
- minY: -top + delta.y,
- maxY: viewport.height - height - top + delta.y
- };
- } else {
- limits.current = {
- minX: rectLimits.left - left + delta.x,
- maxX: rectLimits.right - width - left + delta.x,
- minY: rectLimits.top - top + delta.y,
- maxY: rectLimits.bottom - height - top + delta.y
- };
- }
- }
-
- onDragStart && onDragStart(event);
- }
- }, [targetRef, handleRef, onDragStart, enabled, keepInViewport, delta, rectLimits]);
-
- /**
- * Subscribe to mouse/touch events to drag and stop dragging.
- */
- useEffect(() => {
- if (dragging) {
- document.addEventListener('mousemove', reposition, { passive: true });
- document.addEventListener('touchmove', reposition, { passive: true });
- document.addEventListener('mouseup', stopDragging);
- document.addEventListener('touchend', stopDragging);
- } else {
- document.removeEventListener('mousemove', reposition, { passive: true });
- document.removeEventListener('mouseup', stopDragging);
- document.removeEventListener('touchmove', reposition, { passive: true });
- document.removeEventListener('touchend', stopDragging);
- }
-
- return () => {
- document.removeEventListener('mousemove', reposition, { passive: true });
- document.removeEventListener('mouseup', stopDragging);
- document.removeEventListener('touchmove', reposition, { passive: true });
- document.removeEventListener('touchend', stopDragging);
- };
-
- function stopDragging(event) {
- event.preventDefault();
- targetRef.current.style.willChange = '';
- onDragEnd && onDragEnd(event);
-
- setDragging(false);
- setPrevious(reposition(event));
- }
-
- function reposition(event) {
- const source = (event.changedTouches && event.changedTouches[0]) || (event.touches && event.touches[0]) || event;
- const { clientX, clientY } = source;
- const x = clientX - initial.current.x + previous.x;
- const y = clientY - initial.current.y + previous.y;
-
- const newDelta = calculateDelta({ x, y, limits: limits.current });
-
- setDelta(newDelta);
- onDrag && onDrag(event);
-
- return newDelta;
- }
- }, [targetRef, onDrag, onDragEnd, handleRef, dragging, previous, keepInViewport, rectLimits]);
-
- /**
- * Listen to delta drag changes and set the target position.
- */
- useEffect(() => {
- if (targetRef.current) {
- targetRef.current.style.transform = `translate(${delta.x}px, ${delta.y}px)`;
- }
- }, [targetRef, delta]);
-
- /**
- * Listen to drag start/stop and update DOM values.
- */
- useEffect(() => {
- const handle = handleRef.current || targetRef.current;
-
- if (handle) {
- handle.style.cursor = dragging ? 'grabbing' : 'move';
- }
-
- if (targetRef.current) {
- targetRef.current.setAttribute('aria-grabbed', dragging);
- }
-
- if (dragging) {
- DomHandler.addClass(document.body, 'p-unselectable-text');
- } else {
- DomHandler.removeClass(document.body, 'p-unselectable-text');
- }
- }, [targetRef, handleRef, dragging]);
-
- const calculateDelta = ({ x, y, limits }) => {
- if (!limits) {
- return { x, y };
- }
-
- const { minX, maxX, minY, maxY } = limits;
-
- return {
- x: Math.min(Math.max(x, minX), maxX),
- y: Math.min(Math.max(y, minY), maxY)
- };
- };
-
- const resetState = useCallback(() => {
- setDelta({ x: 0, y: 0 });
- setPrevious({ x: 0, y: 0 });
- }, [setDelta, setPrevious]);
-
- return { dragging, delta, resetState };
-};
diff --git a/components/lib/utils/DomHandler.js b/components/lib/utils/DomHandler.js
index 10073a1c7e..5c27fdba91 100644
--- a/components/lib/utils/DomHandler.js
+++ b/components/lib/utils/DomHandler.js
@@ -115,10 +115,14 @@ export default class DomHandler {
}
static getViewport() {
- const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
- const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
-
- return { width: vw, height: vh };
+ let win = window,
+ d = document,
+ e = d.documentElement,
+ g = d.getElementsByTagName('body')[0],
+ w = win.innerWidth || e.clientWidth || g.clientWidth,
+ h = win.innerHeight || e.clientHeight || g.clientHeight;
+
+ return { width: w, height: h };
}
static getOffset(el) {