diff --git a/package-lock.json b/package-lock.json index 549bbe4..bde4b9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,10 @@ "version": "6.6.0", "license": "Apache-2.0", "dependencies": { + "@floating-ui/dom": "^1.5.4", "body-scroll-lock": "^4.0.0-beta.0", "classnames": "^2.3.2", - "framer-motion": "^10.16.16", - "popper.js": "^1.16.1" + "framer-motion": "^10.16.16" }, "devDependencies": { "@storybook/addon-docs": "^7.0.18", @@ -2357,6 +2357,28 @@ "dev": true, "license": "MIT" }, + "node_modules/@floating-ui/core": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.3.tgz", + "integrity": "sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q==", + "dependencies": { + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.4.tgz", + "integrity": "sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ==", + "dependencies": { + "@floating-ui/core": "^1.5.3", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "dev": true, @@ -15019,14 +15041,6 @@ "node": ">=10" } }, - "node_modules/popper.js": { - "version": "1.16.1", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, "node_modules/posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -21636,6 +21650,28 @@ "version": "2.1.2", "dev": true }, + "@floating-ui/core": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.3.tgz", + "integrity": "sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q==", + "requires": { + "@floating-ui/utils": "^0.2.0" + } + }, + "@floating-ui/dom": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.4.tgz", + "integrity": "sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ==", + "requires": { + "@floating-ui/core": "^1.5.3", + "@floating-ui/utils": "^0.2.0" + } + }, + "@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "dev": true, @@ -30269,9 +30305,6 @@ "@babel/runtime": "^7.17.8" } }, - "popper.js": { - "version": "1.16.1" - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", diff --git a/package.json b/package.json index 56b9ab0..366b115 100644 --- a/package.json +++ b/package.json @@ -40,10 +40,10 @@ }, "homepage": "https://github.com/reaviz/rdk#readme", "dependencies": { + "@floating-ui/dom": "^1.5.4", "body-scroll-lock": "^4.0.0-beta.0", "classnames": "^2.3.2", - "framer-motion": "^10.16.16", - "popper.js": "^1.16.1" + "framer-motion": "^10.16.16" }, "peerDependencies": { "react": ">=16", diff --git a/src/Overlay/ConnectedOverlay/ConnectedOverlay.tsx b/src/Overlay/ConnectedOverlay/ConnectedOverlay.tsx index 7e66545..d47fa9c 100644 --- a/src/Overlay/ConnectedOverlay/ConnectedOverlay.tsx +++ b/src/Overlay/ConnectedOverlay/ConnectedOverlay.tsx @@ -4,18 +4,13 @@ import React, { useEffect, Fragment, forwardRef, - Ref, - useImperativeHandle, useMemo } from 'react'; import { TriggerTypes, OverlayTrigger } from '../OverlayTrigger'; import { Placement, ReferenceProp } from '../../Position'; import { AnimatePresence } from 'framer-motion'; import { OverlayContext } from '../../Overlay/OverlayContext'; -import { - ConnectedOverlayContent, - ConnectedOverlayContentRef -} from './ConnectedOverlayContent'; +import { ConnectedOverlayContent } from './ConnectedOverlayContent'; export interface OverlayEvent { /** @@ -116,11 +111,7 @@ export interface ConnectedOverlayProps { onClose?: (event?: any) => void; } -export const ConnectedOverlay: FC< - ConnectedOverlayProps & { - ref?: Ref; - } -> = forwardRef( +export const ConnectedOverlay: FC = forwardRef( ( { reference, @@ -134,19 +125,12 @@ export const ConnectedOverlay: FC< onClose, ...rest }, - ref + _ref ) => { const mounted = useRef(false); const overlayTriggerRef = useRef(null); - const contentRef = useRef(null); const triggerRef = reference || overlayTriggerRef; - useImperativeHandle(ref, () => ({ - updatePosition: () => { - contentRef.current?.updatePosition(); - } - })); - useEffect(() => { if (mounted.current) { if (!open) { @@ -194,7 +178,6 @@ export const ConnectedOverlay: FC< {open && ( diff --git a/src/Overlay/ConnectedOverlay/ConnectedOverlayContent.tsx b/src/Overlay/ConnectedOverlay/ConnectedOverlayContent.tsx index 4b0b957..dd9b660 100644 --- a/src/Overlay/ConnectedOverlay/ConnectedOverlayContent.tsx +++ b/src/Overlay/ConnectedOverlay/ConnectedOverlayContent.tsx @@ -1,8 +1,6 @@ import React, { FC, forwardRef, - Ref, - useImperativeHandle, RefObject, useEffect, useState, @@ -31,100 +29,92 @@ export interface ConnectedOverlayContentProps { onClose?: (event?: any) => void; } -export const ConnectedOverlayContent: FC< - ConnectedOverlayContentProps & { - ref?: Ref; - } -> = forwardRef( - ( - { - triggerRef, - children, - portalClassName, - closeOnBodyClick, - closeOnEscape, - elementType, - appendToBody, - followCursor, - modifiers, - placement, - onClose - }, - ref - ) => { - const id = useId(); - const [overlayIndex, setOverlayIndex] = useState(null); - const [positionRef, popperRef] = usePosition(triggerRef, { - followCursor, - modifiers, - placement - }); - - useImperativeHandle(ref, () => ({ - updatePosition: () => { - popperRef?.current?.scheduleUpdate(); - } - })); +export const ConnectedOverlayContent: FC = + forwardRef( + ( + { + triggerRef, + children, + portalClassName, + closeOnBodyClick, + closeOnEscape, + elementType, + appendToBody, + followCursor, + modifiers, + placement, + onClose + }, + _ref + ) => { + const id = useId(); + const [overlayIndex, setOverlayIndex] = useState(null); + const positionRef = usePosition(triggerRef, { + followCursor, + modifiers, + placement + }); - const onClickOutside = useCallback( - (event: any) => { - if (closeOnBodyClick) { - // don't fire if i click the clicker - let ref: HTMLElement | null = null; - if ((triggerRef as RefObject).current) { - ref = (triggerRef as RefObject).current as HTMLElement; - } else if ((triggerRef as HTMLElement).contains !== undefined) { - ref = triggerRef as HTMLElement; - } + const onClickOutside = useCallback( + (event: any) => { + if (closeOnBodyClick) { + // don't fire if i click the clicker + let ref: HTMLElement | null = null; + if ((triggerRef as RefObject).current) { + ref = (triggerRef as RefObject) + .current as HTMLElement; + } else if ((triggerRef as HTMLElement).contains !== undefined) { + ref = triggerRef as HTMLElement; + } - // Handle parent click containers - const container = event.target.closest('.rdk-portal'); + // Handle parent click containers + const container = event.target.closest('.rdk-portal'); - // Only close the last one - const isLast = portals.indexOf(id) === portals.length - 1; + // Only close the last one + const isLast = portals.indexOf(id) === portals.length - 1; - if (!ref?.contains(event.target) && (isLast || !container)) { - onClose?.(event); + if (!ref?.contains(event.target) && (isLast || !container)) { + onClose?.(event); + } } - } - }, - [closeOnBodyClick, onClose] - ); + }, + [closeOnBodyClick, onClose] + ); - const onEscape = useCallback(() => { - if (closeOnEscape) { - onClose?.(); - } - }, [closeOnEscape, onClose]); + const onEscape = useCallback(() => { + if (closeOnEscape) { + onClose?.(); + } + }, [closeOnEscape, onClose]); - useExitListener({ - open: true, - ref: positionRef, - onClickOutside, - onEscape - }); + useExitListener({ + open: true, + ref: positionRef, + onClickOutside, + onEscape + }); - useEffect(() => { - if (positionRef && overlayIndex) { - positionRef.current.style.zIndex = overlayIndex; - } - }, [positionRef.current, overlayIndex]); + useEffect(() => { + if (positionRef && overlayIndex) { + positionRef.current.style.zIndex = overlayIndex; + } + }, [positionRef.current, overlayIndex]); - return ( - setOverlayIndex(event.overlayIndex)} - onUnmount={() => setOverlayIndex(null)} - > - {children} - - ); - } -); + return ( + setOverlayIndex(event.overlayIndex)} + onUnmount={() => setOverlayIndex(null)} + > + {children} + + ); + } + ); ConnectedOverlayContent.defaultProps = { closeOnBodyClick: true, diff --git a/src/Position/Position.story.tsx b/src/Position/Position.story.tsx index 3a5e752..ee5a7af 100644 --- a/src/Position/Position.story.tsx +++ b/src/Position/Position.story.tsx @@ -9,7 +9,7 @@ const meta: Meta = { export const Simple = { render: () => { const anchorRef = useRef(null); - const [positionRef] = usePosition(anchorRef, { placement: 'bottom' }); + const positionRef = usePosition(anchorRef, { placement: 'bottom' }); return (
{ const elementRef = useRef(null); - const popper = useRef(null); const mouse = useRef<{ pageX: number; pageY: number }>({ pageX: 0, pageY: 0 }); - // Find the real reference pointer for updating - const refPointer = (reference as RefObject).current; - const popperRef = useMemo(() => { const refObj = reference as RefObject; if (refObj.current !== undefined) { @@ -73,41 +76,47 @@ export const usePosition = ( } return refElement; - }, [followCursor, reference, refPointer, mouse]); + }, [followCursor, (reference as RefObject)?.current, mouse]); useLayoutEffect(() => { let rqf; const onMouseMove = ({ pageX, pageY }: MouseEvent) => { mouse.current = { pageX, pageY }; - popper.current?.scheduleUpdate(); }; const onWindowScroll = () => { - rqf = requestAnimationFrame(() => { - popper.current?.scheduleUpdate(); - }); + rqf = requestAnimationFrame(() => {}); }; if (elementRef.current && popperRef) { + Object.assign(elementRef.current.style, { + width: 'max-content', + position: 'absolute', + top: 0, + left: 0 + }); + //@ts-ignore - popper.current = new PopperJS(popperRef, elementRef.current, { + computePosition(popperRef, elementRef.current, { placement: placement || 'top', - modifiers: modifiers || {}, - onCreate: () => { - window.addEventListener('scroll', onWindowScroll); - - if (followCursor) { - window.addEventListener('mousemove', onMouseMove); - } - } + middleware: modifiers || [flip(), shift({ limiter: limitShift() })] + }).then(({ x, y }) => { + Object.assign(elementRef.current.style, { + left: `${x}px`, + top: `${y}px` + }); }); + + window.addEventListener('scroll', onWindowScroll); + + if (followCursor) { + window.addEventListener('mousemove', onMouseMove); + } } return () => { if (!elementRef.current) { - popper.current?.destroy(); - cancelAnimationFrame(rqf); window.removeEventListener('scroll', onWindowScroll); @@ -118,12 +127,5 @@ export const usePosition = ( }; }, [elementRef.current]); - useLayoutEffect(() => { - if (popper.current) { - popper.current.reference = popperRef as any; - popper.current.scheduleUpdate(); - } - }, [popperRef]); - - return [elementRef, popper]; + return elementRef; };