theKashey / react-focus-lock

It is a trap! A lock for a Focus. 🔓
MIT License
1.27k stars 67 forks source link

Is there any functionality similar to inert? #319

Open MrOxMasTer opened 1 month ago

MrOxMasTer commented 1 month ago

add for FocusLock itself when disabled, be able to make all components inert. I can't pass this attribute. But I can for the div below it.

image

I'm asking because maybe there is already a similar functionality, but I don't see it.

theKashey commented 1 month ago

Quite a strange feature request. Why do you want to inert content? You should be able to pass any attribute following the following pattern

<FocusLock lockProps={{inert: isOpen ? undefined : ''}} />

Note that in React (and preact) implementation inert is not a boolean prop.

MrOxMasTer commented 1 month ago

Quite a strange feature request. Why do you want to inert content? You should be able to pass any attribute following the following pattern

The problem is that you don't always need to mount/unmount a component. For example a menu component implemented with html/CSS. Especially in server/client combination in next.js

I just wanted to have a flag like: disabledAddInert, but your option is not bad either.

The prop disabled has functionality built in and it works the way I need it to when js is disabled/enabled.

But to do it this way is quite inconvenient and looks unpleasant:

export const Menu = () => {
  const [isOpened, setIsOpened] = useState<boolean | null>(null);

  useLayoutEffect(() => setIsOpened(false), []); 

 ...

 return (
    <>
      ...
        <FocusLock
          lockProps={{ inert: isOpened === null ? false : !isOpened }}
          disabled={!isOpened}
        >
              ...
        </FocusLock>
      </aside>
    </>
}

The thing is that if I set the same condition as for disabled, namely !isOpened, then with the default false/null/undefined, AND with js disabled, it will treat it as true. So when I disable js, it becomes inert=true. This doesn't happen with disabled

To solve this problem, I need another type to compare it with, like null and if js is on the client, then use useEffect/useLayoutEffect and write this horror:

useLayoutEffect(() => setIsOpened(false), []);

I don't know how FocusLock works under the hood, but it would be handy if I didn't have to write a hook every time.

theKashey commented 1 month ago

using useLayoutEffect seems to be fair solution for it. Not sure how else it could be.

Frankly this functionality should be build somewhere before or after FocusLock as it sounds a little unrelated. Sorry, I still cannot understand the usecase.

MrOxMasTer commented 1 month ago

Frankly this functionality should be build somewhere before or after FocusLock as it sounds a little unrelated. Sorry, I still cannot understand the usecase.

I want js to be enabled:

With js: FocusLock turned off:

I just want inert to depend on disabled, as there is a built-in functionality for that, I don't see the point of writing an extra useLayoutEffect.

and whatever you have to invent with these types null / false.

theKashey commented 1 month ago

You are missing the key point here - the gap between "no js" and "js". Think "hydration"

//-- some file
export const HydrationContext = createContext(false);

export const isHydrated = () => useContext(HydrationContext);

//-- nextjs layout
"use client";

export const Layout = ({children}) => {
   const [hydrated,setHydrated] = useState(false);

   useEffect(() => setHydrated(true), []);

   return <HydrationContext value={hydrated}>{children}</HydrationContext>
}

// your code
        <FocusLock
          lockProps={{ inert: !isOpened && isHydrated() ? '' : undefined}}
          disabled={!isOpened}
        >

That's it. This is not your particular component to handle this moment, nor FocusLock's. Dont forget about main principles and you were right not liking useLayoutEffect in the incorrect place.