usePositioner#
usePositioner is the complete anchored-overlay positioning hook the library's
own Popover, Tooltip, Menu, Select, and Combobox parts are built on. It composes
usePosition (floating placement), usePresence (the open/close lifecycle),
top-layer promotion, and the user-agent [popover] style neutralization into a
single hook, so a custom anchored overlay does not have to rediscover the
platform quirks the library already handles.
See also: usePosition, usePresence, useDismiss.
Demo#
import { usePositioner } from 'hono-preact-ui';
import { useRef, useState } from 'preact/hooks';
// A complete custom anchored overlay built directly on usePositioner: it composes
// floating placement, the open/close presence lifecycle, native top-layer
// promotion (Popover API), and the UA [popover] style resets, so the demo only
// wires open state and side-aware styling. positionerProps already carries the
// merged ref, position style, and data-side/data-align. Styling: .docs-usepositioner*.
export function UsePositionerDemo() {
const anchorRef = useRef<HTMLButtonElement>(null);
const floatingRef = useRef<HTMLElement>(null);
const [open, setOpen] = useState(false);
const { isPresent, positionerProps, state } = usePositioner({
open,
anchorRef,
floatingRef,
side: 'bottom',
align: 'center',
offset: 8,
mount: 'unmount',
});
return (
<div class="docs-usepositioner">
<button
ref={anchorRef}
type="button"
class="docs-usepositioner-trigger"
onClick={() => setOpen((o) => !o)}
>
{open ? 'Close' : 'Open'} overlay
</button>
{isPresent ? (
<div {...positionerProps}>
<div class="docs-usepositioner-popup" data-side={state.side}>
Built on usePositioner, anchored {state.side}.
</div>
</div>
) : null}
</div>
);
}
Example#
import { usePositioner } from 'hono-preact-ui';
import { useRef } from 'preact/hooks';
function Anchored({ open }: { open: boolean }) {
const anchorRef = useRef<HTMLButtonElement>(null);
const floatingRef = useRef<HTMLElement>(null);
const { isPresent, positionerProps, state } = usePositioner({
open,
anchorRef,
floatingRef,
side: 'bottom',
align: 'center',
offset: 8,
mount: 'unmount',
});
return (
<>
<button ref={anchorRef}>anchor</button>
{isPresent && (
<div {...positionerProps} data-side={state.side}>
overlay contents
</div>
)}
</>
);
}
Signature#
import { usePositioner } from 'hono-preact-ui';
function usePositioner(opts: UsePositionerOptions): UsePositionerResult;
Options#
| Option | Type | Notes |
|---|---|---|
open | boolean | The overlay's open state; drives the presence lifecycle. |
anchorRef | RefObject<HTMLElement> | The element the overlay is positioned against. |
floatingRef | RefObject<HTMLElement> | The positioned overlay element. |
side | Side | Preferred side: top, right, bottom, or left. |
align | Align | Alignment along the side: start, center, or end. |
offset | number | Gap in pixels between anchor and overlay. |
getAnchorRect | ClientRectGetter | Optional. Position against a point or virtual element instead of anchorRef (e.g. a pointer position). |
mount | 'unmount' | 'hidden' | 'unmount': branch on isPresent and return null while closed. 'hidden': keep the element mounted and hidden while closed. |
usePositioner returns { isPresent, positionerProps, state, position, arrowRef }:
spread positionerProps onto your positioner element (it already carries the
merged ref including floatingRef, Popover-API promotion, the UA [popover]
style resets, and data-side / data-align), branch on isPresent when mount
is 'unmount', and read state.side / state.align for side-aware styling. For
a custom arrow, attach the returned arrowRef to your arrow element and read
position.arrowX / position.arrowY for its offset (this is what the built-in
Arrow part does).