diff --git a/docs/components/figures/menu/usage-document.html b/docs/components/figures/menu/usage-document.html
new file mode 100644
index 0000000000..77803b58ce
--- /dev/null
+++ b/docs/components/figures/menu/usage-document.html
@@ -0,0 +1,30 @@
+
diff --git a/docs/components/images/menu/usage-document.webp b/docs/components/images/menu/usage-document.webp
new file mode 100644
index 0000000000..8a50cf8040
Binary files /dev/null and b/docs/components/images/menu/usage-document.webp differ
diff --git a/docs/components/menu.md b/docs/components/menu.md
index 44635dfc22..d835bc7834 100644
--- a/docs/components/menu.md
+++ b/docs/components/menu.md
@@ -61,7 +61,7 @@ choices on a temporary surface.
When opened, menus position themselves to an anchor. Thus, either `anchor` or
`anchorElement` must be supplied to `md-menu` before opening. Additionally, a
shared parent of `position:relative` should be present around the menu and it's
-anchor.
+anchor, because the default menu is positioned relative to the anchor element.
Menus also render menu items such as `md-menu-item` and handle keyboard
navigation between `md-menu-item`s as well as typeahead functionality.
@@ -215,14 +215,14 @@ Granny Smith, and Red Delicious."](images/menu/usage-submenu.webp)
```
-### Fixed menus
+### Fixed-positioned menus
Internally menu uses `position: absolute` by default. Though there are cases
when the anchor and the node cannot share a common ancestor that is `position:
relative`, or sometimes, menu will render below another item due to limitations
with `position: absolute`. In most of these cases, you would want to use the
`positioning="fixed"` attribute to position the menu relative to the window
-instead of relative to the parent.
+instead of relative to the element.
> Note: Fixed menu positions are positioned relative to the window and not the
> document. This means that the menu will not scroll with the anchor as the page
@@ -264,6 +264,64 @@ Cucumber."](images/menu/usage-fixed.webp)
```
+### Document-positioned menus
+
+When set to `positioning="document"`, `md-menu` will position itself relative to
+the document as opposed to the element or the window from `"absolute"` and
+`"fixed"` values respectively.
+
+Document level positioning is useful for the following cases:
+
+- There are no ancestor elements that produce a `relative` positioning
+ context.
+ - `position: relative`
+ - `position: absolute`
+ - `position: fixed`
+ - `transform: translate(x, y)`
+ - etc.
+- The menu is hoisted to the top of the DOM
+ - The last child of ``
+ - [Top layer](https://developer.mozilla.org/en-US/docs/Glossary/Top_layer)
+
+- The same `md-menu` is being reused and the contents and anchors are being
+ dynamically changed
+
+
+
+!["A filled button that says open document menu. There is an open menu anchored
+to the bottom of the button with three items, Apple, Banana, and
+Cucumber."](images/menu/usage-document.webp)
+
+
+
+
+```html
+
+
+ Open document menu
+
+
+
+
+
+ Apple
+
+
+ Banana
+
+
+ Cucumber
+
+
+
+
+```
+
## Accessibility
By default Menu is set up to function as a `role="menu"` with children as
@@ -395,7 +453,6 @@ a sharp 0px border radius.](images/menu/theming.webp)
## API
-
### MdMenu <md-menu>
#### Properties
diff --git a/menu/demo/demo.ts b/menu/demo/demo.ts
index fb861ad649..453a578d02 100644
--- a/menu/demo/demo.ts
+++ b/menu/demo/demo.ts
@@ -64,10 +64,11 @@ const collection = new MaterialCollection>(
}),
new Knob('positioning', {
defaultValue: 'absolute' as const,
- ui: selectDropdown<'absolute' | 'fixed'>({
+ ui: selectDropdown<'absolute' | 'fixed' | 'document'>({
options: [
{label: 'absolute', value: 'absolute'},
{label: 'fixed', value: 'fixed'},
+ {label: 'document', value: 'document'},
],
}),
}),
diff --git a/menu/demo/stories.ts b/menu/demo/stories.ts
index 7d2f7eb2e1..e6ecfac021 100644
--- a/menu/demo/stories.ts
+++ b/menu/demo/stories.ts
@@ -22,7 +22,7 @@ export interface StoryKnobs {
anchorCorner: Corner | undefined;
menuCorner: Corner | undefined;
defaultFocus: FocusState | undefined;
- positioning: 'absolute' | 'fixed' | undefined;
+ positioning: 'absolute' | 'fixed' | 'document' | undefined;
open: boolean;
quick: boolean;
hasOverflow: boolean;
@@ -98,7 +98,10 @@ const standard: MaterialStoryInit = {
render(knobs) {
return html`
-
+
= {
return html`
-
+
= {
return html`
-
+
= {
],
render(knobs) {
return html`
-
+
This is the anchor (use the "open" knob)
` to render over everything or in a top-layer.
+ * - You are reusing a single `md-menu` element that dynamically renders
+ * content.
*/
- @property() positioning: 'absolute' | 'fixed' = 'absolute';
+ @property() positioning: 'absolute' | 'fixed' | 'document' = 'absolute';
/**
* Skips the opening and closing animations.
*/
@@ -229,6 +239,11 @@ export abstract class Menu extends LitElement {
* The event path of the last window pointerdown event.
*/
private pointerPath: EventTarget[] = [];
+
+ /**
+ * Whether or not the menu is repositoining due to window / document resize
+ */
+ private isRepositioning = false;
private readonly openCloseAnimationSignal = createAnimationSignal();
private readonly listController = new ListController