Popover
Show content with a trigger in a floating pane
- Optional arrow to point to content.
- Keeps within bounds of page.
- Can be placed into 12 anchor positions. Popovers are a great way to show content that's only visible when trigger is pressed, floating above the current content. They automatically stack above other content. Be sure to open the code example above for a copy-paste implementation.
Note: Popovers are not a recommended pattern for mobile apps. Instead you can use Adapt and render them as a Sheet, or just conditionally render them to some native UI.
Installation
Popover is already installed in @hanzo/gui, or you can install it independently:
npm install @hanzogui/popoverFor native apps, we recommend setting up native portals to preserve React context inside Popover content.
Anatomy
import { Popover, Adapt, Sheet } from '@hanzo/gui' // or '@hanzogui/popover'
export default () => (
<Popover>
<Popover.Trigger />
{/* Optional: Control focus behavior */}
<Popover.FocusScope loop trapped focusOnIdle={true}>
<Popover.Content>
<Popover.Arrow />
<Popover.Close />
{/* ScrollView is optional, can just put any contents inside if not scrollable */}
<Popover.ScrollView>{/* ... */}</Popover.ScrollView>
{/* ... */}
</Popover.Content>
</Popover.FocusScope>
{/* optionally change to sheet when small screen */}
{/* you can also use <Popover.Adapt /> */}
<Adapt when="max-md">
<Sheet>
<Sheet.Overlay />
<Sheet.Frame>
<Sheet.ScrollView>
<Adapt.Contents />
</Sheet.ScrollView>
</Sheet.Frame>
</Sheet>
</Adapt>
</Popover>
)Scoping
Popover supports scoping which lets you mount one or more Popover instances at the root of your app, while having a deeply nested child Trigger or Content attach to the proper parent Popover instance.
In performance sensitive areas you may want to take advantage of this as it allows you to only render the Popover.Trigger inside the sensitive area. Popover isn't the cheapest component - it has a lot of functionality inside of it like scroll management, focus management, and tracking position.
Here's the basic anatomy of using scope and placing your Popover higher up
for performance:
import { Popover } from '@hanzo/gui'
// in your root layout:
export default ({ children }) => (
<Popover scope="user-avatar">
<Popover.Content>
<Popover.Arrow />
<Popover.Close />
<Popover.ScrollView>{/* ... */}</Popover.ScrollView>
</Popover.Content>
{/* the rest of your app, note that it's inside of Popover */}
{children}
</Popover>
)export default () => (
<Popover.Trigger scope="user-avatar">
<Avatar />
</Popover.Trigger>
)Note that the Trigger scope ties to the Popover scope.
API Reference
Popover
Contains every component for the popover.
| Prop | Type | Default | Required |
|---|---|---|---|
| children | React.ReactNode | - | ✓ |
| size | SizeTokens | - | - |
| placement | Placement | - | - |
| open | boolean | - | - |
| defaultOpen | boolean | - | - |
| onOpenChange | (open: boolean, via?: 'hover' | 'press') => void | - | - |
| keepChildrenMounted | boolean | "lazy" | - | - |
| disableDismissable | boolean | - | - |
| stayInFrame | ShiftProps | boolean | { padding: 10 } | - |
| allowFlip | FlipProps | boolean | - | - |
| offset | OffsetOptions | - | - |
| hoverable | boolean | UseFloatingProps | - | - |
| resize | SizeProps | boolean | - | - |
| zIndex | number | - | - |
For most of these properties, you'll want to reference the floating-ui docs.
If using modal={true} (which is true by default), refer to the PortalProvider
installation for more information.
Popover.Arrow
Popover.Arrow can be used to show an arrow that points at the Trigger element. In order for the Arrow to show you must have a Trigger element within your Popover. Arrows extend YStack, see Stacks.
| Prop | Type | Default | Required |
|---|---|---|---|
| animatePosition | boolean | - | - |
| size | SizeTokens | - | - |
| offset | number | - | - |
Popover.Trigger
Used to trigger opening of the popover when uncontrolled, just renders a YStack, see Stacks.
Popover.Content
Extends PopperContent which extends SizableStack which extends a YStack (see Stacks). Used to display the content of the popover.
| Prop | Type | Default | Required |
|---|---|---|---|
| animatePosition | boolean | 'even-when-repositioning' | - | - |
| transformOrigin | boolean | true | - |
| size | SizeTokens | - | - |
| unstyled | boolean | - | |
| trapFocus | boolean | - | |
| disableFocusScope | boolean | - | |
| onOpenAutoFocus | FocusScopeProps['onMountAutoFocus'] | - | |
| onCloseAutoFocus | FocusScopeProps['onUnmountAutoFocus'] | false | - | |
| lazyMount | boolean | - | - |
Popover.Anchor
Renders as YStack, see Stacks.
When you want the Trigger to be in another location from where the Popover attaches, use Anchor. When used, Anchor is where the Popover will attach, while Trigger will open it.
Sheet (with Adapt)
When used with Adapt, you can render a Sheet when that breakpoint is
active. Import Sheet directly from @hanzo/gui or @hanzogui/sheet.
See Sheet for more props.
Must use Adapt.Contents inside the Sheet.Frame to insert the
contents given to Popover.Content
Popover.FocusScope
Provides access to the underlying FocusScope component used by Popover for focus management. Can be used to control focus behavior from a parent component.
| Prop | Type | Default | Required |
|---|---|---|---|
| enabled | boolean | true | - |
| loop | boolean | false | - |
| trapped | boolean | false | - |
| focusOnIdle | boolean | number | false | - |
| onMountAutoFocus | (event: Event) => void | - | - |
| onUnmountAutoFocus | (event: Event) => void | - | - |
Popover.ScrollView
Must be nested inside Content. Renders as a plain React Native ScrollView. If
used alongside <Adapt /> and Sheet, Hanzo GUI will automatically know to remove
this ScrollView when swapping into the Sheet, as the Sheet must use its own
ScrollView that handles special logic for interactions with dragging.
Utility Functions
These functions allow you to programmatically manage open popovers.
import {
closeOpenPopovers,
closeLastOpenedPopover,
hasOpenPopovers,
} from '@hanzogui/popover'closeOpenPopovers
Closes all currently open popovers. Returns true if any popovers were closed, false if none were open.
const didClose = closeOpenPopovers()closeLastOpenedPopover
Closes only the most recently opened popover. Returns true if a popover was closed, false if none were open.
const didClose = closeLastOpenedPopover()hasOpenPopovers
Returns true if there are any open popovers, false otherwise.
if (hasOpenPopovers()) {
// handle open popovers
}Last updated on