floating-ui icon indicating copy to clipboard operation
floating-ui copied to clipboard

feat: SafePolygon component

Open atomiks opened this issue 3 years ago • 11 comments

Closes #1811

This exports a new SafePolygon component that can be used to block pointer-events as the cursor traverses it, a better solution to the current global "holes" strategy due to side effects

Usage:

const {polygonState, safePolygon} = useSafePolygon();

useHover(context, {
  handleClose: safePolygon()
});

return (
  <>
    {/* Avoids containing blocks messing with the position, not always required */}
    <FloatingPortal>
      {open && <SafePolygon state={polygonState} />}
    </FloatingPortal>
    {/* reference / floating stuff here */}
  </>
);

atomiks avatar Jul 26 '22 19:07 atomiks

There is no change log for this pull request yet.

Create a changelog

rollingversions[bot] avatar Jul 26 '22 19:07 rollingversions[bot]

Deploy Preview for vibrant-gates-22c214 canceled.

Name Link
Latest commit e7f74b2c42b803dd34291cfa96da714ee8053440
Latest deploy log https://app.netlify.com/sites/vibrant-gates-22c214/deploys/62e13926bd64b00008a5b622

netlify[bot] avatar Jul 26 '22 19:07 netlify[bot]

@mihkeleidast

atomiks avatar Jul 26 '22 19:07 atomiks

A thought on the new component API: as a use, it is not too comfortable to have to use a separate outside state to save the points and pass it on to SafePolygon.

Could it use the context instead? Passing the context to different components seems to be an existing practice:

useHover(context, {
  handleClose: safePolygon(),
});

return (
  <>
    {/* Avoids containing blocks messing with the position, not always required */}
    <FloatingPortal>
      {open && <SafePolygon context={context} />}
    </FloatingPortal>
    {/* reference / floating stuff here */}
  </>
);

As an alternative, maybe create a hook that saves the state internally:

const { SafePolygon, safePolygon } = useSafePolygon();

useHover(context, {
  handleClose: safePolygon(),
});

return (
  <>
    {/* Avoids containing blocks messing with the position, not always required */}
    <FloatingPortal>
      {open && <SafePolygon />}
    </FloatingPortal>
    {/* reference / floating stuff here */}
  </>
);

or

const { points, safePolygon } = useSafePolygon();

useHover(context, {
  handleClose: safePolygon(),
});

return (
  <>
    {/* Avoids containing blocks messing with the position, not always required */}
    <FloatingPortal>
      {open && <SafePolygon points={points} />}
    </FloatingPortal>
    {/* reference / floating stuff here */}
  </>
);

Do I also understand correctly that the SafePolygon is not rendered as a child of the floating element, rather it should be aside it?

mihkeleidast avatar Jul 27 '22 06:07 mihkeleidast

useSafePolygon() is indeed much better. This is breaking in React 17 and 18 when using flushSync for some reason due to onScrollMouseLeave(), will debug after and try to refactor to a hook

atomiks avatar Jul 27 '22 09:07 atomiks

How's this, you still import SafePolygon as a component, but now the other one is returned from the hook with points state internal within it.

const {polygonState, safePolygon} = useSafePolygon();

useHover(context, {
  handleClose: safePolygon(),
});

return open && <SafePolygon state={polygonState} />;

atomiks avatar Jul 27 '22 13:07 atomiks

I think this is a good API 👍

mihkeleidast avatar Jul 27 '22 13:07 mihkeleidast

Alternative using spreadable props which is slightly more ergonomic imo:

const {polygonProps, safePolygon} = useSafePolygon();

useHover(context, {
  handleClose: safePolygon(),
});

return open && <SafePolygon {...polygonProps} />;

atomiks avatar Jul 27 '22 13:07 atomiks

Unfortunately this has a problem in Firefox where it won't block pointer-events when the polygon gets generated on the mouseleave for 1 frame ~half the time, doesn't happen in Chrome or Safari 😒 , not sure how to fix it.

atomiks avatar Jul 27 '22 20:07 atomiks

How to reproduce that issue? Would investigate a bit, but not sure what exactly to look for.

mihkeleidast avatar Jul 28 '22 10:07 mihkeleidast

Add the polygon component to the Menu.tsx component file in the playground. Add another nested submenu right under the "Share..." one. Remove restMs: 25 from the safePolygon() call (non-0 is broken right now)

In Firefox, you can see a 1 frame flicker. This also causes issues seen at the end:

https://user-images.githubusercontent.com/22450188/181524306-82ba4d9f-2e48-45a3-afa6-577fd6bba3bc.mov

atomiks avatar Jul 28 '22 14:07 atomiks

This one get stalled? :(

maccman avatar Sep 25 '22 20:09 maccman

https://github.com/floating-ui/floating-ui/pull/2069

atomiks avatar Dec 24 '22 12:12 atomiks