|
| 1 | +--- |
| 2 | +title: ActionMenu |
| 3 | +status: Alpha |
| 4 | +source: https://github.com/primer/react/tree/main/src/ActionMenu |
| 5 | +storybook: '/react/storybook?path=/story/composite-components-actionmenu2' |
| 6 | +description: An ActionMenu is an ActionList-based component for creating a menu of actions that expands through a trigger button. |
| 7 | +--- |
| 8 | + |
| 9 | +import {Box, Avatar} from '@primer/components' |
| 10 | +import {ActionMenu, ActionList} from '@primer/components/drafts' |
| 11 | +import {Props} from '../../src/props' |
| 12 | + |
| 13 | +<br /> |
| 14 | + |
| 15 | +<Box sx={{border: '1px solid', borderColor: 'border.default', borderRadius: 2, padding: 6}}> |
| 16 | +<ActionMenu> |
| 17 | + <ActionMenu.Button>Menu</ActionMenu.Button> |
| 18 | + <ActionList> |
| 19 | + <ActionList.Item onSelect={() => onSelect('Copy link')}> |
| 20 | + Copy link |
| 21 | + <ActionList.TrailingVisual>⌘C</ActionList.TrailingVisual> |
| 22 | + </ActionList.Item> |
| 23 | + <ActionList.Item onSelect={() => onSelect('Quote reply')}> |
| 24 | + Quote reply |
| 25 | + <ActionList.TrailingVisual>⌘Q</ActionList.TrailingVisual> |
| 26 | + </ActionList.Item> |
| 27 | + <ActionList.Item onSelect={() => onSelect('Edit comment')}> |
| 28 | + Edit comment |
| 29 | + <ActionList.TrailingVisual>⌘E</ActionList.TrailingVisual> |
| 30 | + </ActionList.Item> |
| 31 | + <ActionList.Divider /> |
| 32 | + <ActionList.Item variant="danger" onSelect={() => onSelect('Delete file')}> |
| 33 | + Delete file |
| 34 | + <ActionList.TrailingVisual>⌘D</ActionList.TrailingVisual> |
| 35 | + </ActionList.Item> |
| 36 | + </ActionList> |
| 37 | + |
| 38 | +</ActionMenu> |
| 39 | +</Box> |
| 40 | + |
| 41 | +<br /> |
| 42 | + |
| 43 | +```js |
| 44 | +import {ActionMenu} from '@primer/components/drafts' |
| 45 | +``` |
| 46 | + |
| 47 | +<br /> |
| 48 | + |
| 49 | +## Examples |
| 50 | + |
| 51 | +### Minimal example |
| 52 | + |
| 53 | +`ActionMenu` ships with `ActionMenu.Button` which is an accessible trigger for the overlay. It's recommended to compose `ActionList` with this component. |
| 54 | + |
| 55 | + |
| 56 | + |
| 57 | +```javascript live noinline |
| 58 | +// import {ActionMenu, ActionList} from '@primer/components/drafts' |
| 59 | +const {ActionMenu, ActionList} = drafts // ignore docs silliness; import like that ↑ |
| 60 | + |
| 61 | +render( |
| 62 | + <ActionMenu> |
| 63 | + <ActionMenu.Button>Menu</ActionMenu.Button> |
| 64 | + |
| 65 | + <ActionList> |
| 66 | + <ActionList.Item onSelect={event => console.log('New file')}>New file</ActionList.Item> |
| 67 | + <ActionList.Item>Copy link</ActionList.Item> |
| 68 | + <ActionList.Item>Edit file</ActionList.Item> |
| 69 | + <ActionList.Divider /> |
| 70 | + <ActionList.Item variant="danger">Delete file</ActionList.Item> |
| 71 | + </ActionList> |
| 72 | + </ActionMenu> |
| 73 | +) |
| 74 | +``` |
| 75 | + |
| 76 | +### With a custom anchor |
| 77 | + |
| 78 | +You can choose to have a different _anchor_ for the Menu dependending on the application's context. |
| 79 | + |
| 80 | + |
| 81 | + |
| 82 | +```javascript live noinline |
| 83 | +// import {ActionMenu, ActionList} from '@primer/components/drafts' |
| 84 | +const {ActionMenu, ActionList} = drafts // ignore docs silliness; import like that ↑ |
| 85 | + |
| 86 | +render( |
| 87 | + <ActionMenu> |
| 88 | + <ActionMenu.Anchor> |
| 89 | + <ButtonInvisible aria-label="Open column options"> |
| 90 | + <KebabHorizontalIcon /> |
| 91 | + </ButtonInvisible> |
| 92 | + </ActionMenu.Anchor> |
| 93 | + |
| 94 | + <ActionList> |
| 95 | + <ActionList.Item> |
| 96 | + <ActionList.LeadingVisual> |
| 97 | + <PencilIcon /> |
| 98 | + </ActionList.LeadingVisual> |
| 99 | + Rename |
| 100 | + </ActionList.Item> |
| 101 | + <ActionList.Item> |
| 102 | + <ActionList.LeadingVisual> |
| 103 | + <ArchiveIcon /> |
| 104 | + </ActionList.LeadingVisual> |
| 105 | + Archive all cards |
| 106 | + </ActionList.Item> |
| 107 | + <ActionList.Item variant="danger"> |
| 108 | + <ActionList.LeadingVisual> |
| 109 | + <TrashIcon /> |
| 110 | + </ActionList.LeadingVisual> |
| 111 | + Delete |
| 112 | + </ActionList.Item> |
| 113 | + </ActionList> |
| 114 | + </ActionMenu> |
| 115 | +) |
| 116 | +``` |
| 117 | + |
| 118 | +### With Groups |
| 119 | + |
| 120 | +```javascript live noinline |
| 121 | +// import {ActionMenu, ActionList} from '@primer/components/drafts' |
| 122 | +const {ActionMenu, ActionList} = drafts // ignore docs silliness; import like that ↑ |
| 123 | + |
| 124 | +render( |
| 125 | + <ActionMenu> |
| 126 | + <ActionMenu.Button>Open column menu</ActionMenu.Button> |
| 127 | + |
| 128 | + <ActionList showDividers> |
| 129 | + <ActionList.Group title="Live query"> |
| 130 | + <ActionList.Item> |
| 131 | + <ActionList.LeadingVisual> |
| 132 | + <SearchIcon /> |
| 133 | + </ActionList.LeadingVisual> |
| 134 | + repo:github/memex,github/github |
| 135 | + </ActionList.Item> |
| 136 | + </ActionList.Group> |
| 137 | + <ActionList.Divider /> |
| 138 | + <ActionList.Group title="Layout" variant="subtle"> |
| 139 | + <ActionList.Item> |
| 140 | + <ActionList.LeadingVisual> |
| 141 | + <NoteIcon /> |
| 142 | + </ActionList.LeadingVisual> |
| 143 | + Table |
| 144 | + <ActionList.Description variant="block"> |
| 145 | + Information-dense table optimized for operations across teams |
| 146 | + </ActionList.Description> |
| 147 | + </ActionList.Item> |
| 148 | + <ActionList.Item role="listitem"> |
| 149 | + <ActionList.LeadingVisual> |
| 150 | + <ProjectIcon /> |
| 151 | + </ActionList.LeadingVisual> |
| 152 | + Board |
| 153 | + <ActionList.Description variant="block">Kanban-style board focused on visual states</ActionList.Description> |
| 154 | + </ActionList.Item> |
| 155 | + </ActionList.Group> |
| 156 | + <ActionList.Divider /> |
| 157 | + <ActionList.Group> |
| 158 | + <ActionList.Item> |
| 159 | + <ActionList.LeadingVisual> |
| 160 | + <FilterIcon /> |
| 161 | + </ActionList.LeadingVisual> |
| 162 | + Save sort and filters to current view |
| 163 | + </ActionList.Item> |
| 164 | + <ActionList.Item> |
| 165 | + <ActionList.LeadingVisual> |
| 166 | + <FilterIcon /> |
| 167 | + </ActionList.LeadingVisual> |
| 168 | + Save sort and filters to new view |
| 169 | + </ActionList.Item> |
| 170 | + </ActionList.Group> |
| 171 | + <ActionList.Divider /> |
| 172 | + <ActionList.Group> |
| 173 | + <ActionList.Item> |
| 174 | + <ActionList.LeadingVisual> |
| 175 | + <GearIcon /> |
| 176 | + </ActionList.LeadingVisual> |
| 177 | + View settings |
| 178 | + </ActionList.Item> |
| 179 | + </ActionList.Group> |
| 180 | + </ActionList> |
| 181 | + </ActionMenu> |
| 182 | +) |
| 183 | +``` |
| 184 | + |
| 185 | +### With External Anchor |
| 186 | + |
| 187 | +To create an anchor outside of the menu, you need to switch to controlled mode for the menu and pass it as `anchorRef` to `ActionMenu`: |
| 188 | + |
| 189 | +```javascript live noinline |
| 190 | +// import {ActionMenu, ActionList} from '@primer/components/drafts' |
| 191 | +const {ActionMenu, ActionList} = drafts // ignore docs silliness; import like that ↑ |
| 192 | + |
| 193 | +const Example = () => { |
| 194 | + const [open, setOpen] = React.useState(false) |
| 195 | + const anchorRef = React.createRef() |
| 196 | + |
| 197 | + return ( |
| 198 | + <> |
| 199 | + <Button ref={anchorRef} onClick={() => setOpen(!open)}> |
| 200 | + {open ? 'Close Menu' : 'Open Menu'} |
| 201 | + </Button> |
| 202 | + |
| 203 | + <ActionMenu open={open} onOpenChange={setOpen} anchorRef={anchorRef}> |
| 204 | + <ActionList> |
| 205 | + <ActionList.Item>Copy link</ActionList.Item> |
| 206 | + <ActionList.Item>Quote reply</ActionList.Item> |
| 207 | + <ActionList.Item>Edit comment</ActionList.Item> |
| 208 | + <ActionList.Divider /> |
| 209 | + <ActionList.Item variant="danger">Delete file</ActionList.Item> |
| 210 | + </ActionList> |
| 211 | + </ActionMenu> |
| 212 | + </> |
| 213 | + ) |
| 214 | +} |
| 215 | + |
| 216 | +render(<Example />) |
| 217 | +``` |
| 218 | + |
| 219 | +## Props / API reference |
| 220 | + |
| 221 | +### ActionMenu |
| 222 | + |
| 223 | +| Name | Type | Default | Description | |
| 224 | +| :----------- | :-------------------------------------------------- | :-----: | :----------------------------------------------------------------------------------------------------------------------- | |
| 225 | +| children\* | `React.ReactElement[]` | - | Required. Recommended: `ActionMenu.Button` or `ActionMenu.Anchor` with [`ActionList`](/drafts/ActionList2) | |
| 226 | +| open | `boolean` | - | Optional. If defined, will control the open/closed state of the overlay. Must be used in conjuction with `onOpenChange`. | |
| 227 | +| onOpenChange | `(open: boolean) => void` | - | Optional. If defined, will control the open/closed state of the overlay. Must be used in conjuction with `open`. | |
| 228 | +| anchorRef | `React.RefObject<HTMLElement>` | - | Optional. Useful for defining an external anchor | |
| 229 | +| overlayProps | [`Partial<OverlayProps>`](/Overlay#component-props) | - | Optional. Props to be spread on the internal [`AnchoredOverlay`](/AnchoredOverlay) component. | |
| 230 | + |
| 231 | +### ActionMenu.Button |
| 232 | + |
| 233 | +| Type | Default | Description | |
| 234 | +| :-------------------------------------- | :-----: | :------------------------------------------------------------------------------------------------------------------- | |
| 235 | +| [ButtonProps](/Buttons#component-props) | - | Optional. You can pass all of the props that you would pass to a [`Button`](/Buttons) component like `variant`, `sx` | |
| 236 | + |
| 237 | +### ActionMenu.Anchor |
| 238 | + |
| 239 | +| Name | Type | Default | Description | |
| 240 | +| :--------- | :------------------- | :-----: | :-------------------------------- | |
| 241 | +| children\* | `React.ReactElement` | - | Required. Accepts a single child. | |
| 242 | + |
| 243 | +## Further reading |
| 244 | + |
| 245 | +[Interface guidelines: Action List + Menu](https://primer.style/design/components/action-list) |
| 246 | + |
| 247 | +## Related components |
| 248 | + |
| 249 | +- [ActionList](/drafts/ActionList2) |
| 250 | +- [DropdownMenu](/DropdownMenu) |
| 251 | +- [SelectPanel](/SelectPanel) |
0 commit comments