Skip to content

Floating layers ​

Every popup, menu, tooltip and dialog of the library is portaled out to <body>. Its DOM is elsewhere, but its React events still bubble through the tree it was declared in. A canvas that treats a pointer event as its own needs to tell the two apart.

tsx
import { cameThroughPortal, isInFloatingLayer } from '@basmilius/react-ui';
tsx
import { useState, type PointerEvent } from 'react';
import { Button, Menu, isInFloatingLayer } from '@basmilius/react-ui';

export default function FloatingLayersDemo() {
    const [clicks, setClicks] = useState(0);

    const onPointerDown = (event: PointerEvent<HTMLDivElement>): void => {
        // A popup is portaled out of this box, yet its events still bubble here through React.
        if (isInFloatingLayer(event.target)) {
            return;
        }
        setClicks((count) => count + 1);
    };

    return (
        <div onPointerDown={onPointerDown} className="grid h-44 w-full max-w-md place-items-center rounded-lg border border-dashed border-border-strong">
            <div className="flex flex-col items-center gap-2 text-xs text-text-muted">
                The canvas took {clicks} clicks.
                <Menu.Root>
                    <Menu.Trigger render={<Button variant="secondary" size="sm" />}>Open a menu</Menu.Trigger>
                    <Menu.Popup>
                        <Menu.Item>A click in here is not the canvas's</Menu.Item>
                    </Menu.Popup>
                </Menu.Root>
            </div>
        </div>
    );
}

isInFloatingLayer(target) says whether an event's target lies in a popup, a tooltip or a dialog of the library, rather than on the surface under it. Clicking the menu row above does not count as a click on the canvas.

cameThroughPortal(event) says whether an event reached currentTarget through a portal, with its target inside the element in React's tree but not in the DOM. Use it for a handler that should only hear events from its own DOM.