Dm0r18
Dm0r18

Reputation: 1

Headless UI Popover - Disable body scrolling when opening menu

my goal is, when the navigation menu is opened and the popover gets displayed, to disable scrolling on the page. And then, on close, restore scrolling.
I write in typescript using nextJs, tailwind css, framer motion, next-intl and of course headless-ui (all last releases).
Couldn't seem to find any similar question or solution.
Thanks for your time and help current interface with scrolling always enabled

Here is my code with a trivial solution I found...

import {
    Popover,
    PopoverBackdrop,
    PopoverButton,
    PopoverPanel,
} from '@headlessui/react';
import { EnvelopeOpenIcon } from '@heroicons/react/20/solid';
import { Bars3Icon, ChevronUpIcon } from '@heroicons/react/24/solid';
import { AnimatePresence, motion } from 'framer-motion';
import { Button } from './Button';
import { MobileNavLinks } from './NavLinks';

export default function MobileNavigation() {
    return (
        <Popover>
            {({ open }) => {
                document.body.style.overflow = open ? 'hidden' : 'auto';
                console.log(open);
                return (
                <>
                    <PopoverButton
                        className="relative flex size-12 z-10 items-center justify-center rounded-lg stroke-mcm p-2 hover:bg-gray-200/50 hover:stroke-gray-600 active:stroke-gray-900 ui-not-focus-visible:outline-none focus-visible:outline-none"
                        aria-label="Toggle site navigation"
                    >
                        {({ open }) =>
                            open ? (
                                <ChevronUpIcon className="h-7 w-7" />
                            ) : (
                                <Bars3Icon className="h-7 w-7" />
                            )
                        }
                    </PopoverButton>
                    <AnimatePresence initial={false}>
                        {open && (
                            <>
                                <PopoverBackdrop
                                    static
                                    as={motion.div}
                                    initial={{ opacity: 0 }}
                                    animate={{ opacity: 1 }}
                                    exit={{ opacity: 0 }}
                                    className="fixed inset-0 z-0 bg-gray-300/60 backdrop-blur"
                                />
                                <PopoverPanel
                                    static
                                    as={motion.div}
                                    initial={{ opacity: 0, y: -32 }}
                                    animate={{ opacity: 1, y: 0 }}
                                    exit={{
                                        opacity: 0,
                                        y: -32,
                                        transition: { duration: 0.2 },
                                    }}
                                    className="absolute inset-x-0 top-0 z-0 origin-top rounded-b-2xl bg-gray-50 px-6 pb-6 pt-24 shadow-2xl shadow-gray-900/20"
                                >
                                    <div className="space-y-4">
                                        <MobileNavLinks />
                                        <PopoverButton
                                            as={Button}
                                            className="sm:hidden"
                                            label="contacts"
                                            icon={
                                                <EnvelopeOpenIcon className="h-7 w-7" />
                                            }
                                            color="blue"
                                        ></PopoverButton>
                                    </div>
                                </PopoverPanel>
                            </>
                        )}
                    </AnimatePresence>
                </>
            );}
}
        </Popover>
    );
}

document.body.style.overflow = open ? 'hidden' : 'auto'; works but the evaluation get's called multiple times during React's re-render cycles.
Could't figure out how to integrate useEffect() in here, without incurring in errors like:
React Hook "useEffect" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.eslintreact-hooks/rules-of-hooks

Upvotes: 0

Views: 371

Answers (0)

Related Questions