Skip to content

Commit baa330f

Browse files
authored
feat: Add RAC example of Menu nested in popover dialog (#7402)
* feat: Add RAC example of Menu nested in popover dialog * remove unused import * Improve dark mode
1 parent 62718d4 commit baa330f

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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>
56 KB
Loading

0 commit comments

Comments
 (0)