Installation
pnpm dlx @docyrus/cli add @docyrus/rn-morph-popoverpnpm add react-native-reanimatedThe trigger is measured with measureInWindow. A transparent Modal then shows a panel that starts at the trigger's size and corner radius and grows to the content size, while the content fades and slides in. Closing plays the animation in reverse. Tapping outside the panel closes it.
The panel opens on side and flips to the other side when there is not enough room. It is centred on the trigger and kept 16pt from the screen edges. If the content changes height while open, the panel resizes in place. When the system's reduce-motion setting is on, the panel appears without animation.
Usage
import { MorphPopover } from '@/components/docyrus-native/morph-popover';
<MorphPopover>
<Text className="text-sm font-semibold text-white dark:text-neutral-900">Quick actions</Text>
</MorphPopover><MorphPopover
side="bottom"
triggerWidth={140}
triggerHeight={40}
contentWidth={280}
bgClassName="bg-primary"
isOpen={open}
onOpenChange={setOpen}
trigger={<Text className="text-primary-foreground">New item</Text>}>
<Button onPress={() => setOpen(false)}>Task</Button>
</MorphPopover>Content text does not inherit colour on React Native, so style the children to contrast with bgClassName. The default background is bg-neutral-900 in light mode and bg-neutral-100 in dark mode.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Panel content (required) |
trigger | ReactNode | - | Trigger content (default: a plus icon) |
triggerSize | number | 44 | Square trigger size (px) |
triggerWidth | number | - | Trigger width (overrides triggerSize) |
triggerHeight | number | - | Trigger height (overrides triggerSize) |
triggerRadius | number | triggerSize / 2 (6 with custom dimensions) | Trigger corner radius |
triggerClassName | string | - | Trigger classes |
disabled | boolean | false | Disables the trigger |
isOpen | boolean | - | Controlled open state |
onOpenChange | (open: boolean) => void | - | Open state change callback |
side | 'top' | 'bottom' | 'top' | Preferred side; flips when there is not enough room |
sideOffset | number | 24 | Gap between trigger and panel (px) |
contentWidth | number | 240 | Panel width, clamped to screen width minus a 16pt gutter |
speed | number | 0.25 | Animation duration in seconds |
bgClassName | string | 'bg-neutral-900 dark:bg-neutral-100' | Background classes shared by trigger and panel |
contentClassName | string | - | Panel content classes |
className | string | - | Wrapper classes |
Type Exports
| Type | Description |
|---|---|
MorphPopoverProps | Props for MorphPopover |
Differences from web
- GSAP is replaced by Reanimated. The SVG "goo" filter has no React Native equivalent and is not reproduced.
speedis still in seconds.- Outside clicks and Escape become a backdrop tap and the Android back button.