Skip to content

Commit e7080ca

Browse files
committed
Better modality
1 parent d8e55b5 commit e7080ca

File tree

7 files changed

+133
-70
lines changed

7 files changed

+133
-70
lines changed

docs/src/app/(private)/experiments/menubar.tsx

Lines changed: 63 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import classes from './menubar.module.css';
99

1010
export default function MenubarExperiment() {
1111
return (
12-
<div>
12+
<div style={{ isolation: 'isolate' }}>
1313
<h1>Menubar</h1>
1414
<Menubar.Root className={classes.Root}>
1515
<Menu.Root>
@@ -148,62 +148,68 @@ export default function MenubarExperiment() {
148148
</Menubar.Root>
149149
<hr className={classes.Separator} />
150150
<input className={classes.Input} placeholder="focus tester" />
151-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Explicabo quidem,
152-
magnam, doloribus obcaecati culpa nemo provident omnis enim accusamus itaque,
153-
illum in veritatis fugiat quo voluptatibus iusto nam est iure. Lorem ipsum
154-
dolor, sit amet consectetur adipisicing elit. At unde perspiciatis iste autem
155-
totam blanditiis atque, eos suscipit. Nesciunt quod soluta itaque expedita
156-
consequuntur illo similique reprehenderit eligendi iusto perspiciatis placeat
157-
mollitia eos iste obcaecati veniam omnis dolorum doloribus voluptate,
158-
doloremque quo necessitatibus aliquid? Officia perspiciatis necessitatibus
159-
ratione laudantium suscipit enim exercitationem voluptatem, iure vero optio
160-
placeat. Cumque delectus atque dolores optio, quis fugiat in sit architecto
161-
deserunt et, illum non aspernatur quibusdam dolorum saepe minus neque amet.
162-
Ipsum dignissimos, minima iste saepe placeat perferendis ad ducimus quibusdam
163-
deleniti ab est dolorum tenetur praesentium soluta! Voluptatem animi
164-
accusantium vitae! Expedita! Lorem ipsum dolor, sit amet consectetur
165-
adipisicing elit. At unde perspiciatis iste autem totam blanditiis atque, eos
166-
suscipit. Nesciunt quod soluta itaque expedita consequuntur illo similique
167-
reprehenderit eligendi iusto perspiciatis placeat mollitia eos iste obcaecati
168-
veniam omnis dolorum doloribus voluptate, doloremque quo necessitatibus
169-
aliquid? Officia perspiciatis necessitatibus ratione laudantium suscipit enim
170-
exercitationem voluptatem, iure vero optio placeat. Cumque delectus atque
171-
dolores optio, quis fugiat in sit architecto deserunt et, illum non aspernatur
172-
quibusdam dolorum saepe minus neque amet. Ipsum dignissimos, minima iste saepe
173-
placeat perferendis ad ducimus quibusdam deleniti ab est dolorum tenetur
174-
praesentium soluta! Voluptatem animi accusantium vitae! Expedita! Lorem ipsum
175-
dolor, sit amet consectetur adipisicing elit. At unde perspiciatis iste autem
176-
totam blanditiis atque, eos suscipit. Nesciunt quod soluta itaque expedita
177-
consequuntur illo similique reprehenderit eligendi iusto perspiciatis placeat
178-
mollitia eos iste obcaecati veniam omnis dolorum doloribus voluptate,
179-
doloremque quo necessitatibus aliquid? Officia perspiciatis necessitatibus
180-
ratione laudantium suscipit enim exercitationem voluptatem, iure vero optio
181-
placeat. Cumque delectus atque dolores optio, quis fugiat in sit architecto
182-
deserunt et, illum non aspernatur quibusdam dolorum saepe minus neque amet.
183-
Ipsum dignissimos, minima iste saepe placeat perferendis ad ducimus quibusdam
184-
deleniti ab est dolorum tenetur praesentium soluta! Voluptatem animi
185-
accusantium vitae! Expedita! Lorem ipsum dolor, sit amet consectetur
186-
adipisicing elit. At unde perspiciatis iste autem totam blanditiis atque, eos
187-
suscipit. Nesciunt quod soluta itaque expedita consequuntur illo similique
188-
reprehenderit eligendi iusto perspiciatis placeat mollitia eos iste obcaecati
189-
veniam omnis dolorum doloribus voluptate, doloremque quo necessitatibus
190-
aliquid? Officia perspiciatis necessitatibus ratione laudantium suscipit enim
191-
exercitationem voluptatem, iure vero optio placeat. Cumque delectus atque
192-
dolores optio, quis fugiat in sit architecto deserunt et, illum non aspernatur
193-
quibusdam dolorum saepe minus neque amet. Ipsum dignissimos, minima iste saepe
194-
placeat perferendis ad ducimus quibusdam deleniti ab est dolorum tenetur
195-
praesentium soluta! Voluptatem animi accusantium vitae! Expedita! Lorem ipsum
196-
dolor, sit amet consectetur adipisicing elit. At unde perspiciatis iste autem
197-
totam blanditiis atque, eos suscipit. Nesciunt quod soluta itaque expedita
198-
consequuntur illo similique reprehenderit eligendi iusto perspiciatis placeat
199-
mollitia eos iste obcaecati veniam omnis dolorum doloribus voluptate,
200-
doloremque quo necessitatibus aliquid? Officia perspiciatis necessitatibus
201-
ratione laudantium suscipit enim exercitationem voluptatem, iure vero optio
202-
placeat. Cumque delectus atque dolores optio, quis fugiat in sit architecto
203-
deserunt et, illum non aspernatur quibusdam dolorum saepe minus neque amet.
204-
Ipsum dignissimos, minima iste saepe placeat perferendis ad ducimus quibusdam
205-
deleniti ab est dolorum tenetur praesentium soluta! Voluptatem animi
206-
accusantium vitae! Expedita!
151+
<div style={{ zIndex: 100, position: 'relative' }}>
152+
<a href="#">z-index: 100</a>
153+
</div>
154+
<p>
155+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Explicabo quidem,
156+
magnam, doloribus obcaecati culpa nemo provident omnis enim accusamus itaque,
157+
illum in veritatis fugiat quo voluptatibus iusto nam est iure. Lorem ipsum
158+
dolor, sit amet consectetur adipisicing elit. At unde perspiciatis iste autem
159+
totam blanditiis atque, eos suscipit. Nesciunt quod soluta itaque expedita
160+
consequuntur illo similique reprehenderit eligendi iusto perspiciatis placeat
161+
mollitia eos iste obcaecati veniam omnis dolorum doloribus voluptate,
162+
doloremque quo necessitatibus aliquid? Officia perspiciatis necessitatibus
163+
ratione laudantium suscipit enim exercitationem voluptatem, iure vero optio
164+
placeat. Cumque delectus atque dolores optio, quis fugiat in sit architecto
165+
deserunt et, illum non aspernatur quibusdam dolorum saepe minus neque amet.
166+
Ipsum dignissimos, minima iste saepe placeat perferendis ad ducimus quibusdam
167+
deleniti ab est dolorum tenetur praesentium soluta! Voluptatem animi
168+
accusantium vitae! Expedita! Lorem ipsum dolor, sit amet consectetur
169+
adipisicing elit. At unde perspiciatis iste autem totam blanditiis atque, eos
170+
suscipit. Nesciunt quod soluta itaque expedita consequuntur illo similique
171+
reprehenderit eligendi iusto perspiciatis placeat mollitia eos iste obcaecati
172+
veniam omnis dolorum doloribus voluptate, doloremque quo necessitatibus
173+
aliquid? Officia perspiciatis necessitatibus ratione laudantium suscipit enim
174+
exercitationem voluptatem, iure vero optio placeat. Cumque delectus atque
175+
dolores optio, quis fugiat in sit architecto deserunt et, illum non
176+
aspernatur quibusdam dolorum saepe minus neque amet. Ipsum dignissimos,
177+
minima iste saepe placeat perferendis ad ducimus quibusdam deleniti ab est
178+
dolorum tenetur praesentium soluta! Voluptatem animi accusantium vitae!
179+
Expedita! Lorem ipsum dolor, sit amet consectetur adipisicing elit. At unde
180+
perspiciatis iste autem totam blanditiis atque, eos suscipit. Nesciunt quod
181+
soluta itaque expedita consequuntur illo similique reprehenderit eligendi
182+
iusto perspiciatis placeat mollitia eos iste obcaecati veniam omnis dolorum
183+
doloribus voluptate, doloremque quo necessitatibus aliquid? Officia
184+
perspiciatis necessitatibus ratione laudantium suscipit enim exercitationem
185+
voluptatem, iure vero optio placeat. Cumque delectus atque dolores optio,
186+
quis fugiat in sit architecto deserunt et, illum non aspernatur quibusdam
187+
dolorum saepe minus neque amet. Ipsum dignissimos, minima iste saepe placeat
188+
perferendis ad ducimus quibusdam deleniti ab est dolorum tenetur praesentium
189+
soluta! Voluptatem animi accusantium vitae! Expedita! Lorem ipsum dolor, sit
190+
amet consectetur adipisicing elit. At unde perspiciatis iste autem totam
191+
blanditiis atque, eos suscipit. Nesciunt quod soluta itaque expedita
192+
consequuntur illo similique reprehenderit eligendi iusto perspiciatis placeat
193+
mollitia eos iste obcaecati veniam omnis dolorum doloribus voluptate,
194+
doloremque quo necessitatibus aliquid? Officia perspiciatis necessitatibus
195+
ratione laudantium suscipit enim exercitationem voluptatem, iure vero optio
196+
placeat. Cumque delectus atque dolores optio, quis fugiat in sit architecto
197+
deserunt et, illum non aspernatur quibusdam dolorum saepe minus neque amet.
198+
Ipsum dignissimos, minima iste saepe placeat perferendis ad ducimus quibusdam
199+
deleniti ab est dolorum tenetur praesentium soluta! Voluptatem animi
200+
accusantium vitae! Expedita! Lorem ipsum dolor, sit amet consectetur
201+
adipisicing elit. At unde perspiciatis iste autem totam blanditiis atque, eos
202+
suscipit. Nesciunt quod soluta itaque expedita consequuntur illo similique
203+
reprehenderit eligendi iusto perspiciatis placeat mollitia eos iste obcaecati
204+
veniam omnis dolorum doloribus voluptate, doloremque quo necessitatibus
205+
aliquid? Officia perspiciatis necessitatibus ratione laudantium suscipit enim
206+
exercitationem voluptatem, iure vero optio placeat. Cumque delectus atque
207+
dolores optio, quis fugiat in sit architecto deserunt et, illum non
208+
aspernatur quibusdam dolorum saepe minus neque amet. Ipsum dignissimos,
209+
minima iste saepe placeat perferendis ad ducimus quibusdam deleniti ab est
210+
dolorum tenetur praesentium soluta! Voluptatem animi accusantium vitae!
211+
Expedita!
212+
</p>
207213
</div>
208214
);
209215
}

packages/react/src/menu/positioner/MenuPositioner.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { inertValue } from '../../utils/inertValue';
1515
import { InternalBackdrop } from '../../utils/InternalBackdrop';
1616
import { HTMLElementType, refType } from '../../utils/proptypes';
1717
import { useMenuPortalContext } from '../portal/MenuPortalContext';
18+
import { useMenubarRootContext } from '../../menubar/root/MenubarRootContext';
1819

1920
/**
2021
* Positions the menu popup against the trigger.
@@ -54,11 +55,14 @@ const MenuPositioner = React.forwardRef(function MenuPositioner(
5455
modal,
5556
openReason,
5657
} = useMenuRootContext();
57-
const keepMounted = useMenuPortalContext();
5858

59+
const keepMounted = useMenuPortalContext();
5960
const nodeId = useFloatingNodeId();
6061
const parentNodeId = useFloatingParentNodeId();
6162

63+
const menubarRootContext = useMenubarRootContext(true);
64+
const isInMenubar = menubarRootContext != null;
65+
6266
let computedSide = side;
6367
let computedAlign = align;
6468
if (!side) {
@@ -132,10 +136,17 @@ const MenuPositioner = React.forwardRef(function MenuPositioner(
132136
},
133137
});
134138

139+
const shouldRenderBackdrop =
140+
open &&
141+
!nested &&
142+
((!isInMenubar && modal && openReason !== 'hover') ||
143+
(isInMenubar && menubarRootContext.modal));
144+
const backdropCutout = isInMenubar ? menubarRootContext.contentElement : undefined;
145+
135146
return (
136147
<MenuPositionerContext.Provider value={contextValue}>
137-
{mounted && modal && openReason !== 'hover' && parentNodeId === null && (
138-
<InternalBackdrop inert={inertValue(!open)} />
148+
{shouldRenderBackdrop && (
149+
<InternalBackdrop inert={inertValue(!open)} cutout={backdropCutout} />
139150
)}
140151
<FloatingNode id={nodeId}>
141152
<CompositeList elementsRef={itemDomElements} labelsRef={itemLabels}>

packages/react/src/menu/root/useMenuRoot.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,11 @@ export function useMenuRoot(parameters: useMenuRoot.Parameters): useMenuRoot.Ret
192192
});
193193

194194
const focus = useFocus(floatingRootContext, {
195-
enabled: parentType === 'menubar' && !disabled,
195+
enabled:
196+
parentType === 'menubar' &&
197+
!disabled &&
198+
(parentContext as MenubarRootContext).hasSubmenuOpen &&
199+
!open,
196200
});
197201

198202
const click = useClick(floatingRootContext, {

packages/react/src/menubar/root/MenubarRoot.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { isJSDOM } from '#test-utils';
1010

1111
function TestMenubar() {
1212
return (
13-
<Menubar.Root style={{ maxWidth: '25vw' }}>
13+
<Menubar.Root style={{ maxWidth: '25vw', display: 'flex' }}>
1414
<Menu.Root>
1515
<Menu.Trigger data-testid="file-trigger">File</Menu.Trigger>
1616
<Menu.Portal>

packages/react/src/menubar/root/MenubarRoot.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { useMenubarRoot } from './useMenubarRoot';
1212
import { MenubarRootContext, useMenubarRootContext } from './MenubarRootContext';
1313
import { useForkRef, useScrollLock } from '../../utils';
1414
import { useComponentRenderer } from '../../utils/useComponentRenderer';
15-
import { InternalBackdrop } from '../../utils/InternalBackdrop';
1615

1716
const EMPTY_OBJECT = {};
1817

@@ -49,16 +48,27 @@ const MenubarRoot = React.forwardRef(function MenubarRoot(
4948
render: render ?? 'div',
5049
className,
5150
state: EMPTY_OBJECT,
52-
ref: mergedRef,
5351
extraProps: otherProps,
5452
});
5553

54+
const context = React.useMemo(
55+
() => ({
56+
...menubarRoot,
57+
modal,
58+
}),
59+
[menubarRoot, modal],
60+
);
61+
5662
return (
57-
<MenubarRootContext.Provider value={menubarRoot}>
58-
{modal && menubarRoot.hasSubmenuOpen && <InternalBackdrop />}
63+
<MenubarRootContext.Provider value={context}>
5964
<FloatingTree>
6065
<MenubarContent>
61-
<Composite render={renderElement()} orientation={orientation} loop={loop} />
66+
<Composite
67+
render={renderElement()}
68+
orientation={orientation}
69+
loop={loop}
70+
ref={mergedRef}
71+
/>
6272
</MenubarContent>
6373
</FloatingTree>
6474
</MenubarRootContext.Provider>

packages/react/src/menubar/root/MenubarRootContext.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import * as React from 'react';
22
import { useMenubarRoot } from './useMenubarRoot';
33

4-
export interface MenubarRootContext extends useMenubarRoot.ReturnValue {}
4+
export interface MenubarRootContext extends useMenubarRoot.ReturnValue {
5+
modal: boolean;
6+
}
57

68
export const MenubarRootContext = React.createContext<MenubarRootContext | null>(null);
79

packages/react/src/utils/InternalBackdrop.tsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,55 @@ import PropTypes from 'prop-types';
55
* @ignore - internal component.
66
*/
77
const InternalBackdrop = React.forwardRef(function InternalBackdrop(
8-
props: React.ComponentPropsWithoutRef<'div'>,
8+
props: InternalBackdrop.Props,
99
ref: React.ForwardedRef<HTMLDivElement>,
1010
) {
11+
const { cutout, ...otherProps } = props;
12+
13+
let clipPath: string | undefined;
14+
if (cutout) {
15+
const rect = cutout?.getBoundingClientRect();
16+
clipPath = `polygon(
17+
0% 0%,
18+
100% 0%,
19+
100% 100%,
20+
0% 100%,
21+
0% 0%,
22+
${rect.left}px ${rect.top}px,
23+
${rect.left}px ${rect.bottom}px,
24+
${rect.right}px ${rect.bottom}px,
25+
${rect.right}px ${rect.top}px,
26+
${rect.left}px ${rect.top}px
27+
)`;
28+
}
29+
1130
return (
1231
<div
1332
ref={ref}
1433
role="presentation"
1534
// Ensures Floating UI's outside press detection runs, as it considers
1635
// it an element that existed when the popup rendered.
1736
data-floating-ui-inert
18-
{...props}
37+
{...otherProps}
1938
style={{
2039
position: 'fixed',
2140
inset: 0,
41+
clipPath,
2242
}}
2343
/>
2444
);
2545
});
2646

47+
export namespace InternalBackdrop {
48+
export interface Props extends React.HTMLAttributes<HTMLDivElement> {
49+
/**
50+
* The element to cut out of the backdrop.
51+
* This is useful for allowing certain elements to be interactive while the backdrop is present.
52+
*/
53+
cutout?: HTMLElement | null;
54+
}
55+
}
56+
2757
InternalBackdrop.propTypes /* remove-proptypes */ = {
2858
// ┌────────────────────────────── Warning ──────────────────────────────┐
2959
// │ These PropTypes are generated from the TypeScript type definitions. │

0 commit comments

Comments
 (0)