@pras-ui/popup-menu
v0.0.1
Published
A headless, accessible, and highly customizable multi purpose component(popup menu, context menu) for React.
Maintainers
Readme
@pras-ui/popup-menu
A headless, accessible, and highly customizable multi purpose component(popup menu, context menu) for React.
@pras-ui/popup-menu provides the building blocks to create fully-featured popup menu. It handles all the complex logic, like state management, keyboard navigation, accessibility, and positioning, leaving you to focus on styling.
Features
- Headless & Unstyled: Gives you full control over the appearance.
- Full Keyboard Navigation: Arrow key navigation and shortcuts are supported out of the box.
- Context Menu Mode: Can be triggered by right-clicking.
- Sub-menus: Supports infinitely nested sub-menus.
- Choice Items: Create radio groups or checkbox groups inside your menu.
- RTL Support: Adapts automatically to right-to-left languages.
- Accessible: Follows WAI-ARIA patterns for menus.
Installation
npm install @pras-ui/popup-menuUsage
Here's how to create a basic popup menu. You can use any styling solution you prefer; this example uses simple class names.
import {
PopupMenuRoot,
PopupMenuTrigger,
PopupMenuContent,
PopupMenuItem,
PopupMenuPortal,
} from "@pras-ui/popup-menu";
function MyPopupMenu() {
return (
<PopupMenuRoot>
<PopupMenuTrigger asChild>
<button className="my-trigger-button">Open Menu</button>
</PopupMenuTrigger>
<PopupMenuPortal>
<PopupMenuContent className="my-menu-content" sideOffset={5}>
<PopupMenuItem
className="my-menu-item"
onSelect={() => alert("New Tab")}
>
New Tab
</PopupMenuItem>
<PopupMenuItem
className="my-menu-item"
onSelect={() => alert("New Window")}
>
New Window
</PopupMenuItem>
<PopupMenuItem className="my-menu-item" disabled>
New Incognito Window
</PopupMenuItem>
</PopupMenuContent>
</PopupMenuPortal>
</PopupMenuRoot>
);
}API Reference
PopupMenuRoot
| Prop | Type | Default | Description |
| -------------- | ------------------------- | ----------- | ------------------------------------------------- |
| open | boolean | | The controlled open state of the menu. |
| defaultOpen | boolean | false | The initial open state when uncontrolled. |
| onOpenChange | (open: boolean) => void | | Event handler called when the open state changes. |
| mode | 'default' \| 'context' | 'default' | 'context' mode enables right-click triggering. |
| dir | 'ltr' \| 'rtl' | (inherited) | The reading direction of the menu. |
PopupMenuTrigger
The button or element that toggles the menu. It should be a direct child of PopupMenuRoot. It accepts all props of a standard button element.
| Attributes | Type | Description |
| --------------- | ------------------- | ----------------------------- |
| data-hovered | true \| undefined | Whether the item is hovered. |
| data-focused | true \| undefined | Whether the item is focused. |
| data-disabled | true \| undefined | Whether the item is disabled. |
| data-state | opened \| closed | The open state of the menu. |
PopupMenuPortal
Renders the menu content into a React Portal, ensuring it can overlay other elements. This is recommended to avoid z-index and clipping issues.
PopupMenuContent
| Prop | Type | Default | Description |
| ------------------------- | ---------------------------------------- | ----------- | ------------------------------------------------------------------------ |
| dir | 'ltr' \| 'rtl' | (inherited) | The reading direction of the menu. |
| side | 'top' \| 'bottom' \| 'left' \| 'right' | 'bottom' | The preferred side of the trigger to render the content on. |
| sideOffset | number | 0 | The distance in pixels from the trigger. |
| align | 'start' \| 'center' \| 'end' | 'start' | The preferred alignment of the content relative to the trigger. |
| alignOffset | number | 0 | The distance in pixels from the trigger. |
| collisionBoundary | Boundary \| Boundary[] | | The boundary of the floating element. |
| collisionPadding | number \| Record<Side, number> | 0 | The padding of the floating element. |
| sticky | 'partial' \| 'always' | 'partial' | Whether to sticky the floating element. |
| autoHideOnDetach | boolean | false | Whether to auto hide the floating element on detach. |
| removeScroll | boolean | true | Whether to remove scroll on the floating element. |
| position | 'absolute' \| 'fixed' | 'fixed' | The position of the floating element. |
| updatePositionFrequency | 'lazy' \| 'rapid' | 'lazy' | The frequency of the floating element position update. |
| alwaysRender | boolean | false | Whether to always render the floating element. |
| loop | boolean | true | Whether keyboard navigation should loop around. |
| avoidCollisions | boolean | true | Whether to prevent the content from overlapping with the viewport edges. |
| closeOnScroll | boolean | true | Whether the menu should close when scrolling outside of it. |
| navigationUp | Keys[] \| Keys[][] | [] | Custom key(s) to trigger upward navigation through menu items. |
| navigationDown | Keys[] \| Keys[][] | [] | Custom key(s) to trigger downward navigation through menu items. |
| onEsc | (e: Event) => void | | event handler for the Escape key press. |
| onOutsideInteraction | (e: Event) => void | | event handler for clicks or focus outside the menu. |
| Attributes | Type | Description |
| ------------ | ------------------ | --------------------------- |
| data-state | opened \| closed | The open state of the menu. |
PopupMenuItem
| Prop | Type | Default | Description |
| -------------- | --------------------------------- | ------- | --------------------------------------------------------------------- |
| disabled | boolean | false | Prevents the item from being selected. |
| onClick | (e: MenuItemClickEvent) => void | | Event handler called when the item is selected via click or keyboard. |
| closeOnClick | boolean | true | Whether the menu should close after the item is selected. |
| Attributes | Type | Description |
| --------------- | ------------------- | ----------------------------- |
| data-hovered | true \| undefined | Whether the item is hovered. |
| data-focused | true \| undefined | Whether the item is focused. |
| data-disabled | true \| undefined | Whether the item is disabled. |
PopupSubMenuRoot
| Prop | Type | Default | Description |
| -------------- | ------------------------------ | -------- | ------------------------------------------------------------- |
| open | boolean | | The controlled open state of the sub-menu. |
| defaultOpen | boolean | false | The initial open state when uncontrolled. |
| onOpenChange | (open: boolean) => void | | Event handler called when the open state changes. |
| openStrategy | 'hover' \| 'click' \| 'both' | 'both' | How the sub-menu should be triggered. |
| delay | number | 0 | The delay in milliseconds before the sub-menu opens on hover. |
PopupSubMenuTrigger
| Prop | Type | Default | Description |
| ---------- | --------------------------------- | ------- | --------------------------------------------------------------------- |
| disabled | boolean | false | Prevents the item from being selected. |
| onClick | (e: MenuItemClickEvent) => void | | Event handler called when the item is selected via click or keyboard. |
| Attributes | Type | Description |
| --------------- | ------------------- | ----------------------------- |
| data-hovered | true \| undefined | Whether the item is hovered. |
| data-focused | true \| undefined | Whether the item is focused. |
| data-disabled | true \| undefined | Whether the item is disabled. |
PopupSubMenuContent
The positioned container for the sub-menu's items. It has the same props as PopupMenuContent.
PopupMenuChoice
Used to group a set of PopupMenuChoiceItem components.
| Prop | Type | Default | Description |
| -------------- | ------------------------------------- | ---------- | ---------------------------------------------------- |
| mode | 'single' \| 'multiple' | 'single' | Determines if one or multiple items can be selected. |
| defaultValue | string \| string[] | | The initial value when uncontrolled. |
| onChange | (value: string \| string[]) => void | | Event handler called when the value changes. |
PopupMenuChoiceItem
A selectable item within a PopupMenuChoice group.
| Prop | Type | Description |
| ------- | -------- | -------------------------------------------------------- |
| value | string | The value of the item, used to identify it in the group. |
| Attribute | Type | Description |
| ------------ | ------------------------ | ---------------------- |
| data-state | selected \| unselected | The state of the item. |
PopupMenuChoiceIndicator
A component that displays a checkmark or other icon when its parent PopupMenuChoiceItem is selected. It will be hidden otherwise.
| Attribute | Type | Description |
| ------------ | ------------------------ | ---------------------- |
| data-state | selected \| unselected | The state of the item. |
Other Components
PopupMenuArrow: An optional arrow component that can be placed insidePopupMenuContentto point towards the trigger.PopupMenuGroup: A utility component to visually group items together.PopupMenuLabel: A non-interactive label for aPopupMenuGroup.
