|
| 1 | +{/* Copyright 2024 Adobe. All rights reserved. |
| 2 | +This file is licensed to you under the Apache License, Version 2.0 (the "License"); |
| 3 | +you may not use this file except in compliance with the License. You may obtain a copy |
| 4 | +of the License at http://www.apache.org/licenses/LICENSE-2.0 |
| 5 | +Unless required by applicable law or agreed to in writing, software distributed under |
| 6 | +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS |
| 7 | +OF ANY KIND, either express or implied. See the License for the specific language |
| 8 | +governing permissions and limitations under the License. */} |
| 9 | + |
| 10 | +import {ExampleLayout} from '@react-spectrum/docs'; |
| 11 | +export default ExampleLayout; |
| 12 | + |
| 13 | +import docs from 'docs:react-aria-components'; |
| 14 | +import styles from '@react-spectrum/docs/src/docs.css'; |
| 15 | +import Popover from '@react-spectrum/docs/pages/assets/component-illustrations/Popover.svg'; |
| 16 | +import Dialog from '@react-spectrum/docs/pages/assets/component-illustrations/Dialog.svg'; |
| 17 | +import Menu from '@react-spectrum/docs/pages/assets/component-illustrations/Menu.svg'; |
| 18 | +import Button from '@react-spectrum/docs/pages/assets/component-illustrations/ActionButton.svg'; |
| 19 | +import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; |
| 20 | + |
| 21 | +--- |
| 22 | +keywords: [example, menu, popover, dialog, aria, accessibility, react, component] |
| 23 | +type: component |
| 24 | +image: account-menu.png |
| 25 | +description: A Menu with an interactive header, built with a Dialog and Popover. |
| 26 | +--- |
| 27 | + |
| 28 | +# Account Menu |
| 29 | + |
| 30 | +A [Menu](../Menu.html) with an interactive header, built with a [Dialog](../Dialog.html) and [Popover](../Popover.html). |
| 31 | + |
| 32 | +## Example |
| 33 | + |
| 34 | +For accessibility reasons, standalone Menus cannot contain any children other than menu items (optionally grouped into sections). To build patterns that include such elements, place the Menu into a Dialog with additional interactive elements as siblings. |
| 35 | + |
| 36 | +```tsx import |
| 37 | +import './tailwind.global.css'; |
| 38 | +``` |
| 39 | + |
| 40 | +```tsx example standalone |
| 41 | +import {DialogTrigger, Button, Popover, Dialog, Menu, MenuItem, Separator, Switch, composeRenderProps} from 'react-aria-components'; |
| 42 | +import type {MenuItemProps, SwitchProps} from 'react-aria-components'; |
| 43 | + |
| 44 | +function AccountMenuExample() { |
| 45 | + return ( |
| 46 | + <div className="p-8 bg-gray-50 dark:bg-zinc-900 rounded-lg flex items-start justify-center"> |
| 47 | + <DialogTrigger> |
| 48 | + <Button aria-label="Account" className="inline-flex items-center justify-center rounded-md p-1.5 text-white bg-transparent border-none hover:bg-gray-200 pressed:bg-gray-300 dark:hover:bg-zinc-800 dark:pressed:bg-zinc-700 transition-colors cursor-default outline-none focus-visible:ring-2 focus-visible:ring-blue-600"> |
| 49 | + <img alt="" src="https://i.imgur.com/xIe7Wlb.png" className="w-7 h-7 rounded-full" /> |
| 50 | + </Button> |
| 51 | + <Popover placement="bottom end" className="p-2 overflow-auto rounded-lg bg-white dark:bg-zinc-950 shadow-lg ring-1 ring-black dark:ring-white ring-opacity-10 dark:ring-opacity-15 entering:animate-in entering:fade-in entering:placement-bottom:slide-in-from-top-1 entering:placement-top:slide-in-from-bottom-1 exiting:animate-out exiting:fade-out exiting:placement-bottom:slide-out-to-top-1 exiting:placement-top:slide-out-to-bottom-1 fill-mode-forwards origin-top-left"> |
| 52 | + <Dialog className="outline-none"> |
| 53 | + <div className="flex gap-2 items-center mx-3 mt-2"> |
| 54 | + <img alt="" src="https://i.imgur.com/xIe7Wlb.png" className="w-16 h-16 rounded-full" /> |
| 55 | + <div className="flex flex-col gap-1"> |
| 56 | + <div className="text-[15px] font-bold text-gray-900 dark:text-gray-100 leading-none">Marissa Whitaker</div> |
| 57 | + < div className="text-base text-gray-900 dark:text-gray-100 leading-none mb-1"> [email protected]</ div> |
| 58 | + <MySwitch>Dark Mode</MySwitch> |
| 59 | + </div> |
| 60 | + </div> |
| 61 | + <Separator className="border-none bg-gray-300 dark:bg-zinc-600 h-[1px] mx-3 mt-4 mb-2" /> |
| 62 | + <Menu className="outline-none"> |
| 63 | + <MyMenuItem id="new">Account Settings</MyMenuItem> |
| 64 | + <MyMenuItem id="open">Support</MyMenuItem> |
| 65 | + <Separator className="bg-gray-300 dark:bg-zinc-600 h-[1px] mx-3 my-2" /> |
| 66 | + <MyMenuItem id="save">Legal notices</MyMenuItem> |
| 67 | + <MyMenuItem id="save-as">About</MyMenuItem> |
| 68 | + <Separator className="bg-gray-300 dark:bg-zinc-600 h-[1px] mx-3 my-2" /> |
| 69 | + <MyMenuItem id="print">Sign out</MyMenuItem> |
| 70 | + </Menu> |
| 71 | + </Dialog> |
| 72 | + </Popover> |
| 73 | + </DialogTrigger> |
| 74 | + </div> |
| 75 | + ); |
| 76 | +} |
| 77 | + |
| 78 | +function MyMenuItem(props: MenuItemProps) { |
| 79 | + return <MenuItem {...props} className="group flex w-full items-center rounded-md px-3 py-2 box-border outline-none cursor-default text-gray-900 dark:text-gray-100 focus:bg-blue-500 focus:text-white" />; |
| 80 | +} |
| 81 | + |
| 82 | +function MySwitch(props: SwitchProps) { |
| 83 | + return ( |
| 84 | + <Switch className="group flex gap-2 items-center text-gray-800 dark:text-zinc-200 text-base transition"> |
| 85 | + {composeRenderProps(props.children, children => <> |
| 86 | + <div className="flex h-3 w-6 p-[2px] items-center shrink-0 cursor-default rounded-full transition duration-200 ease-in-out shadow-inner border border-transparent bg-gray-400 dark:bg-zinc-400 group-pressed:bg-gray-500 dark:group-pressed:bg-zinc-300 group-selected:bg-gray-700 group-selected:dark:bg-zinc-300 group-selected:forced-colors:!bg-[Highlight] group-selected:group-pressed:bg-gray-800 group-selected:dark:group-pressed:bg-zinc-200 outline outline-0 outline-blue-600 dark:outline-blue-500 forced-colors:outline-[Highlight] outline-offset-2 group-focus-visible:outline-2"> |
| 87 | + <div className="h-3 w-3 transform rounded-full bg-white dark:bg-zinc-900 outline outline-1 -outline-offset-1 outline-transparent shadow transition duration-200 ease-in-out translate-x-0 group-selected:translate-x-[100%]" /> |
| 88 | + </div> |
| 89 | + {children} |
| 90 | + </>)} |
| 91 | + </Switch> |
| 92 | + ); |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +### Tailwind config |
| 97 | + |
| 98 | +This example uses the following plugins: |
| 99 | + |
| 100 | +* [tailwindcss-react-aria-components](../styling.html#plugin) |
| 101 | +* [tailwindcss-animate](https://github.com/jamiebuilds/tailwindcss-animate) |
| 102 | + |
| 103 | +Add them to your `tailwind.config.js`: |
| 104 | + |
| 105 | +```tsx |
| 106 | +module.exports = { |
| 107 | + // ... |
| 108 | + plugins: [ |
| 109 | + require('tailwindcss-react-aria-components'), |
| 110 | + require('tailwindcss-animate') |
| 111 | + ] |
| 112 | +}; |
| 113 | +``` |
| 114 | + |
| 115 | +## Components |
| 116 | + |
| 117 | +<section className={styles.cardGroup} data-size="small"> |
| 118 | + |
| 119 | +<ExampleCard |
| 120 | + url="../Menu.html" |
| 121 | + title="Menu" |
| 122 | + description="A menu displays a list of actions or options that a user can choose."> |
| 123 | + <Menu style={{background: 'var(--anatomy-gray-100)', width: 'calc(100% - 20px)', padding: 10, maxHeight: 132}} /> |
| 124 | +</ExampleCard> |
| 125 | + |
| 126 | +<ExampleCard |
| 127 | + url="../Button.html" |
| 128 | + title="Button" |
| 129 | + description="A button allows a user to perform an action."> |
| 130 | + <Button /> |
| 131 | +</ExampleCard> |
| 132 | + |
| 133 | +<ExampleCard |
| 134 | + url="../Popover.html" |
| 135 | + title="Popover" |
| 136 | + description="A popover displays content in context with a trigger element."> |
| 137 | + <Popover /> |
| 138 | +</ExampleCard> |
| 139 | + |
| 140 | +<ExampleCard |
| 141 | + url="../Dialog.html" |
| 142 | + title="Dialog" |
| 143 | + description="A dialog is an overlay shown above other content in an application."> |
| 144 | + <Dialog /> |
| 145 | +</ExampleCard> |
| 146 | + |
| 147 | +</section> |
0 commit comments