@m3e/fab-menu 1.0.0-rc.1 → 1.0.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,6 +33,8 @@ declare const M3eFabMenuItemElement_base: import("node_modules/@m3e/core/dist/sr
33
33
  * @attr rel - The relationship between the `target` of the link button and the document.
34
34
  * @attr target - The target of the link button.
35
35
  *
36
+ * @fires click - Emitted when the element is clicked.
37
+ *
36
38
  * @cssprop --m3e-fab-menu-item-height - Height of the menu item.
37
39
  * @cssprop --m3e-fab-menu-item-font-size - Font size of the menu item label.
38
40
  * @cssprop --m3e-fab-menu-item-font-weight - Font weight of the menu item label.
@@ -1 +1 @@
1
- {"version":3,"file":"FabMenuItemElement.d.ts","sourceRoot":"","sources":["../../src/FabMenuItemElement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,cAAc,EAAQ,UAAU,EAAE,cAAc,EAAa,MAAM,KAAK,CAAC;AAiBvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,qBACa,qBAAsB,SAAQ,0BAE1C;;IACC,iCAAiC;IACjC,OAAgB,MAAM,EAAE,cAAc,CA6HpC;IAEF,eAAe,CAAsB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAsB;IACvF,eAAe,CAAuB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAsB;IACxF,eAAe,CAAwB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAuB;IAC3F,eAAe,CAAmB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAmB;IAI9E,wEAAwE;IACxE,IAAI,IAAI,IAAI,iBAAiB,GAAG,IAAI,CAEnC;IAED,kBAAkB;IACT,iBAAiB,IAAI,IAAI;IAKlC,kBAAkB;IACT,oBAAoB,IAAI,IAAI;IAKrC,kBAAkB;cACC,YAAY,CAAC,kBAAkB,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI;IAK/E,kBAAkB;IACT,MAAM,IAAI,OAAO;CAqB3B;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,mBAAmB,EAAE,qBAAqB,CAAC;KAC5C;CACF"}
1
+ {"version":3,"file":"FabMenuItemElement.d.ts","sourceRoot":"","sources":["../../src/FabMenuItemElement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,cAAc,EAAQ,UAAU,EAAE,cAAc,EAAa,MAAM,KAAK,CAAC;AAiBvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,qBACa,qBAAsB,SAAQ,0BAE1C;;IACC,iCAAiC;IACjC,OAAgB,MAAM,EAAE,cAAc,CA6HpC;IAEF,eAAe,CAAsB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAsB;IACvF,eAAe,CAAuB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAsB;IACxF,eAAe,CAAwB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAuB;IAC3F,eAAe,CAAmB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAmB;IAI9E,wEAAwE;IACxE,IAAI,IAAI,IAAI,iBAAiB,GAAG,IAAI,CAEnC;IAED,kBAAkB;IACT,iBAAiB,IAAI,IAAI;IAKlC,kBAAkB;IACT,oBAAoB,IAAI,IAAI;IAKrC,kBAAkB;cACC,YAAY,CAAC,kBAAkB,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI;IAK/E,kBAAkB;IACT,MAAM,IAAI,OAAO;CAqB3B;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,mBAAmB,EAAE,qBAAqB,CAAC;KAC5C;CACF"}
@@ -1,6 +1,6 @@
1
1
  import { CSSResultGroup, LitElement } from "lit";
2
2
  import { M3eFabMenuElement } from "./FabMenuElement";
3
- declare const M3eFabMenuTriggerElement_base: import("node_modules/@m3e/core/dist/src/shared/mixins/Constructor").Constructor<import("@m3e/core").HtmlForMixin> & import("node_modules/@m3e/core/dist/src/shared/mixins/Constructor").Constructor & typeof LitElement;
3
+ declare const M3eFabMenuTriggerElement_base: import("node_modules/@m3e/core/dist/src/shared/mixins/Constructor").Constructor<import("@m3e/core").HtmlForMixin> & typeof LitElement;
4
4
  /**
5
5
  * An element, nested within a clickable element, used to open a floating action button (FAB) menu.
6
6
  *
@@ -1 +1 @@
1
- {"version":3,"file":"FabMenuTriggerElement.d.ts","sourceRoot":"","sources":["../../src/FabMenuTriggerElement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,cAAc,EAAQ,UAAU,EAAE,MAAM,KAAK,CAAC;AAM5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;;AAErD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBACa,wBAAyB,SAAQ,6BAAiC;;IAC7E,iCAAiC;IACjC,OAAgB,MAAM,EAAE,cAAc,CAOpC;IAIF,yCAAyC;IACzC,IAAI,IAAI,IAAI,iBAAiB,GAAG,IAAI,CAEnC;IAED,kBAAkB;IACT,iBAAiB,IAAI,IAAI;IAKlC,kBAAkB;IACT,oBAAoB,IAAI,IAAI;IAKrC,kBAAkB;IACT,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAc3C,kBAAkB;IACT,MAAM,IAAI,IAAI;IAYvB,kBAAkB;cACC,MAAM,IAAI,OAAO;CAUrC;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,sBAAsB,EAAE,wBAAwB,CAAC;KAClD;CACF"}
1
+ {"version":3,"file":"FabMenuTriggerElement.d.ts","sourceRoot":"","sources":["../../src/FabMenuTriggerElement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,cAAc,EAAQ,UAAU,EAAE,MAAM,KAAK,CAAC;AAM5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;;AAErD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBACa,wBAAyB,SAAQ,6BAAmB;;IAC/D,iCAAiC;IACjC,OAAgB,MAAM,EAAE,cAAc,CAOpC;IAIF,yCAAyC;IACzC,IAAI,IAAI,IAAI,iBAAiB,GAAG,IAAI,CAEnC;IAED,kBAAkB;IACT,iBAAiB,IAAI,IAAI;IAKlC,kBAAkB;IACT,oBAAoB,IAAI,IAAI;IAKrC,kBAAkB;IACT,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAc3C,kBAAkB;IACT,MAAM,IAAI,IAAI;IAYvB,kBAAkB;cACC,MAAM,IAAI,OAAO;CAUrC;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,sBAAsB,EAAE,wBAAwB,CAAC;KAClD;CACF"}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@m3e/fab-menu",
3
- "version": "1.0.0-rc.1",
3
+ "version": "1.0.0-rc.3",
4
4
  "description": "FAB Menu for M3E",
5
5
  "author": "matraic <matraic@yahoo.com>",
6
6
  "license": "MIT",
7
- "homepage": "https://matraic.github.io/m3e/",
7
+ "homepage": "https://matraic.github.io/m3e/#/components/fab-menu.html",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/matraic/m3e.git"
@@ -27,8 +27,8 @@
27
27
  "clean": "rimraf dist"
28
28
  },
29
29
  "peerDependencies": {
30
- "@m3e/core": "1.0.0-rc.1",
31
- "@m3e/fab": "1.0.0-rc.1",
30
+ "@m3e/core": "1.0.0-rc.3",
31
+ "@m3e/fab": "1.0.0-rc.3",
32
32
  "lit": "^3.3.0"
33
33
  },
34
34
  "devDependencies": {
package/cem.config.mjs DELETED
@@ -1,16 +0,0 @@
1
- import { customElementVsCodePlugin } from "custom-element-vs-code-integration";
2
-
3
- export default {
4
- globs: ["src/**/*.ts"],
5
- exclude: ["src/**/*.spec.ts"],
6
- packagejson: true,
7
- outdir: "dist",
8
- litelement: true,
9
- plugins: [
10
- customElementVsCodePlugin({
11
- outdir: "dist",
12
- htmlFileName: "html-custom-data.json",
13
- cssFileName: "css-custom-data.json",
14
- }),
15
- ],
16
- };
package/demo/index.html DELETED
@@ -1,60 +0,0 @@
1
- <!doctype html>
2
- <html lang="en" style="overflow-y: auto">
3
- <head>
4
- <title>Floating Action Button Menu for M3E</title>
5
- <meta charset="utf-8" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1" />
7
- <meta name="description" content="Floating Action Button Menu for M3E" />
8
- <base href="./" />
9
- <link rel="preconnect" href="https://fonts.googleapis.com" />
10
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
11
- <link
12
- href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap"
13
- rel="stylesheet"
14
- />
15
- <link
16
- href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0..1,0"
17
- rel="stylesheet"
18
- />
19
- <script type="importmap">
20
- {
21
- "imports": {
22
- "lit": "https://cdn.jsdelivr.net/npm/lit@3.3.0/+esm",
23
- "@m3e/core": "../../core/dist/index.min.js",
24
- "@m3e/core/a11y": "../../core/dist/a11y.min.js",
25
- "@m3e/core/bidi": "../../core/dist/bidi.min.js",
26
- "@m3e/core/anchoring": "../../core/dist/anchoring.min.js"
27
- }
28
- }
29
- </script>
30
- <script type="module" src="../../icon/dist/index.min.js"></script>
31
- <script type="module" src="../../fab/dist/index.min.js"></script>
32
- <script type="module" src="../../theme/dist/index.min.js"></script>
33
- <script type="module" src="../dist/index.min.js"></script>
34
- <style>
35
- body {
36
- font-family: "Roboto";
37
- }
38
- *:not(:defined) {
39
- display: none;
40
- }
41
- </style>
42
- </head>
43
- <body>
44
- <m3e-theme strong-focus>
45
- <m3e-fab variant="primary" size="large" style="position: absolute; right: 1rem; bottom: 1rem">
46
- <m3e-fab-menu-trigger for="fabmenu">
47
- <m3e-icon name="edit"></m3e-icon>
48
- </m3e-fab-menu-trigger>
49
- </m3e-fab>
50
- <m3e-fab-menu id="fabmenu" variant="secondary">
51
- <m3e-fab-menu-item>First</m3e-fab-menu-item>
52
- <m3e-fab-menu-item disabled>Second</m3e-fab-menu-item>
53
- <m3e-fab-menu-item>Third</m3e-fab-menu-item>
54
- <m3e-fab-menu-item>Forth</m3e-fab-menu-item>
55
- <m3e-fab-menu-item>Fifth</m3e-fab-menu-item>
56
- <m3e-fab-menu-item>Sixth</m3e-fab-menu-item>
57
- </m3e-fab-menu>
58
- </m3e-theme>
59
- </body>
60
- </html>
package/eslint.config.mjs DELETED
@@ -1,13 +0,0 @@
1
- import eslint from "@eslint/js";
2
- import tseslint from "typescript-eslint";
3
- import { fileURLToPath } from "url";
4
- import { dirname } from "path";
5
-
6
- export default tseslint.config(eslint.configs.recommended, tseslint.configs.recommended, {
7
- languageOptions: {
8
- parserOptions: {
9
- project: true,
10
- tsconfigRootDir: dirname(fileURLToPath(import.meta.url)),
11
- },
12
- },
13
- });
package/rollup.config.js DELETED
@@ -1,32 +0,0 @@
1
- import resolve from "@rollup/plugin-node-resolve";
2
- import terser from "@rollup/plugin-terser";
3
- import typescript from "@rollup/plugin-typescript";
4
-
5
- const banner = `/**
6
- * @license MIT
7
- * Copyright (c) 2025 matraic
8
- * See LICENSE file in the project root for full license text.
9
- */`;
10
-
11
- export default [
12
- {
13
- input: "src/index.ts",
14
- output: [
15
- {
16
- file: "dist/index.js",
17
- format: "esm",
18
- sourcemap: true,
19
- banner: banner,
20
- },
21
- {
22
- file: "dist/index.min.js",
23
- format: "esm",
24
- sourcemap: true,
25
- banner: banner,
26
- plugins: [terser({ mangle: true })],
27
- },
28
- ],
29
- external: ["@m3e/core", "@m3e/core/a11y", "@m3e/core/bidi", "@m3e/core/anchoring", "@m3e/fab", "lit"],
30
- plugins: [resolve(), typescript()],
31
- },
32
- ];
@@ -1,391 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */
2
- import { css, CSSResultGroup, html, LitElement, unsafeCSS } from "lit";
3
- import { customElement, property } from "lit/decorators.js";
4
-
5
- import { DesignToken, DisabledMixin, Role, ScrollController } from "@m3e/core";
6
- import { RovingTabIndexManager } from "@m3e/core/a11y";
7
- import { M3eDirectionality } from "@m3e/core/bidi";
8
- import { positionAnchor } from "@m3e/core/anchoring";
9
-
10
- import { M3eFabElement } from "@m3e/fab";
11
-
12
- import { FabMenuVariant } from "./FabMenuVariant";
13
- import { M3eFabMenuItemElement } from "./FabMenuItemElement";
14
-
15
- /**
16
- * @summary
17
- * A menu, opened from a floating action button (FAB), used to display multiple related actions.
18
- *
19
- * @description
20
- * The `m3e-fab-menu` component presents a dynamic menu of related actions, elegantly revealed from a
21
- * floating action button (FAB). Designed using expressive, adaptive surfaces, it enables seamless access
22
- * to contextual actions in modern, visually rich interfaces.
23
- *
24
- * @example
25
- * The following example illustrates triggering a `m3e-fab-menu` from an `m3e-fab` using a `m3e-fab-menu-trigger`.
26
- * ```html
27
- * <m3e-fab variant="primary" size="large">
28
- * <m3e-fab-menu-trigger for="fabmenu">
29
- * <m3e-icon name="edit"></m3e-icon>
30
- * </m3e-fab-menu-trigger>
31
- * </m3e-fab>
32
- * <m3e-fab-menu id="fabmenu" variant="secondary">
33
- * <m3e-fab-menu-item>First</m3e-fab-menu-item>
34
- * <m3e-fab-menu-item>Second</m3e-fab-menu-item>
35
- * <m3e-fab-menu-item>Third</m3e-fab-menu-item>
36
- * <m3e-fab-menu-item>Forth</m3e-fab-menu-item>
37
- * <m3e-fab-menu-item>Fifth</m3e-fab-menu-item>
38
- * <m3e-fab-menu-item>Sixth</m3e-fab-menu-item>
39
- * </m3e-fab-menu>
40
- * ```
41
- *
42
- * @tag m3e-fab-menu
43
- *
44
- * @slot - Renders the contents of the menu.
45
- *
46
- * @attr variant - The appearance variant of the menu.
47
- *
48
- * @cssprop --m3e-fab-menu-spacing - Vertical gap between menu items.
49
- * @cssprop --m3e-fab-menu-max-width - Maximum width of the menu.
50
- * @cssprop --m3e-primary-fab-color - Foreground color for primary variant items.
51
- * @cssprop --m3e-primary-fab-container-color - Container color for primary variant items.
52
- * @cssprop --m3e-primary-fab-hover-color - Hover background color for primary variant items.
53
- * @cssprop --m3e-primary-fab-focus-color - Focus background color for primary variant items.
54
- * @cssprop --m3e-primary-fab-ripple-color - Ripple color for primary variant items.
55
- * @cssprop --m3e-secondary-fab-color - Foreground color for secondary variant items.
56
- * @cssprop --m3e-secondary-fab-container-color - Container color for secondary variant items.
57
- * @cssprop --m3e-secondary-fab-hover-color - Hover background color for secondary variant items.
58
- * @cssprop --m3e-secondary-fab-focus-color - Focus background color for secondary variant items.
59
- * @cssprop --m3e-secondary-fab-ripple-color - Ripple color for secondary variant items.
60
- * @cssprop --m3e-tertiary-fab-color - Foreground color for tertiary variant items.
61
- * @cssprop --m3e-tertiary-fab-container-color - Container color for tertiary variant items.
62
- * @cssprop --m3e-tertiary-fab-hover-color - Hover background color for tertiary variant items.
63
- * @cssprop --m3e-tertiary-fab-focus-color - Focus background color for tertiary variant items.
64
- * @cssprop --m3e-tertiary-fab-ripple-color - Ripple color for tertiary variant items.
65
- */
66
- @customElement("m3e-fab-menu")
67
- export class M3eFabMenuElement extends Role(LitElement, "menu") {
68
- /** The styles of the element. */
69
- static override styles: CSSResultGroup = css`
70
- :host {
71
- position: absolute;
72
- flex-direction: column;
73
- row-gap: var(--m3e-fab-menu-spacing, 0.25rem);
74
- padding: unset;
75
- margin: unset;
76
- border: unset;
77
- overflow: visible;
78
- max-width: var(--m3e-fab-menu-max-width, 17.5rem);
79
- opacity: 0;
80
- background-color: transparent;
81
- display: none;
82
- transition: ${unsafeCSS(
83
- `opacity ${DesignToken.motion.spring.fastEffects},
84
- transform ${DesignToken.motion.spring.fastSpatial},
85
- overlay ${DesignToken.motion.spring.fastEffects} allow-discrete,
86
- display ${DesignToken.motion.spring.fastEffects} allow-discrete`
87
- )};
88
- }
89
- .base {
90
- display: contents;
91
- }
92
- :host([variant="primary"]) .base {
93
- --_fab-menu-item-color: var(--m3e-primary-fab-color, ${DesignToken.color.onPrimaryContainer});
94
- --_fab-menu-item-container-color: var(--m3e-primary-fab-container-color, ${DesignToken.color.primaryContainer});
95
- --_fab-menu-background-hover-color: var(--m3e-primary-fab-hover-color, ${DesignToken.color.onPrimaryContainer});
96
- --_fab-menu-background-focus-color: var(--m3e-primary-fab-focus-color, ${DesignToken.color.onPrimaryContainer});
97
- --_fab-menu-ripple-color: var(--m3e-primary-fab-ripple-color, ${DesignToken.color.onPrimaryContainer});
98
- }
99
- :host([variant="secondary"]) .base {
100
- --_fab-menu-item-color: var(--m3e-secondary-fab-color, ${DesignToken.color.onSecondaryContainer});
101
- --_fab-menu-item-container-color: var(
102
- --m3e-secondary-fab-container-color,
103
- ${DesignToken.color.secondaryContainer}
104
- );
105
- --_fab-menu-background-hover-color: var(
106
- --m3e-secondary-fab-hover-color,
107
- ${DesignToken.color.onSecondaryContainer}
108
- );
109
- --_fab-menu-background-focus-color: var(
110
- --m3e-secondary-fab-focus-color,
111
- ${DesignToken.color.onSecondaryContainer}
112
- );
113
- --_fab-menu-ripple-color: var(--m3e-secondary-fab-ripple-color, ${DesignToken.color.onSecondaryContainer});
114
- }
115
- :host([variant="tertiary"]) .base {
116
- --_fab-menu-item-color: var(--m3e-tertiary-fab-color, ${DesignToken.color.onTertiaryContainer});
117
- --_fab-menu-item-container-color: var(--m3e-tertiary-fab-container-color, ${DesignToken.color.tertiaryContainer});
118
- --_fab-menu-background-hover-color: var(--m3e-tertiary-fab-hover-color, ${DesignToken.color.onTertiaryContainer});
119
- --_fab-menu-background-focus-color: var(--m3e-tertiary-fab-focus-color, ${DesignToken.color.onTertiaryContainer});
120
- --_fab-menu-ripple-color: var(--m3e-tertiary-fab-ripple-color, ${DesignToken.color.onTertiaryContainer});
121
- }
122
- :host {
123
- transform: scaleX(0.8);
124
- }
125
- :host(.-left) {
126
- align-items: flex-start;
127
- transform-origin: left;
128
- }
129
- :host(.-right) {
130
- align-items: flex-end;
131
- transform-origin: right;
132
- }
133
- :host(:popover-open) {
134
- transform: scaleX(1);
135
- display: inline-flex;
136
- opacity: 1;
137
- }
138
- :host::backdrop {
139
- background-color: transparent;
140
- }
141
- @starting-style {
142
- :host(:popover-open) {
143
- opacity: 0;
144
- }
145
- :host(:popover-open) {
146
- transform: scaleX(0.8);
147
- }
148
- }
149
- @media (prefers-reduced-motion) {
150
- :host {
151
- transition: none;
152
- }
153
- }
154
- @media (forced-colors: active) {
155
- :host {
156
- border-radius: ${DesignToken.shape.corner.medium};
157
- border: 1px solid MenuText;
158
- background-color: Menu;
159
- }
160
- }
161
- `;
162
-
163
- /** @private */ #fabTabIndex?: number;
164
- /** @private */ #trigger?: HTMLElement;
165
- /** @private */ #anchoringCleanup?: () => void;
166
-
167
- /** @private */
168
- readonly #listManager = new RovingTabIndexManager<LitElement & DisabledMixin>()
169
- .withWrap()
170
- .withHomeAndEnd()
171
- .withVerticalOrientation();
172
-
173
- /** @private */ readonly #keyDownHandler = (e: KeyboardEvent) => this.#handleKeyDown(e);
174
- /** @private */ readonly #documentClickHandler = (e: MouseEvent) => this.#handleDocumentClick(e);
175
-
176
- /** @private */
177
- readonly #scrollController = new ScrollController(this, { target: null, callback: () => this.hide() });
178
-
179
- /** @private */
180
- readonly #toggleHandler = (e: ToggleEvent) => {
181
- if (e.newState === "closed") {
182
- this.#anchoringCleanup?.();
183
- this.#anchoringCleanup = undefined;
184
- } else {
185
- setTimeout(() => {
186
- this.#listManager.setActiveItem(this.#listManager.items.find((x) => !x.disabled));
187
- }, 40);
188
- }
189
- };
190
-
191
- /**
192
- * The appearance variant of the menu.
193
- * @default "primary"
194
- */
195
- @property({ reflect: true }) variant: FabMenuVariant = "primary";
196
-
197
- /** Whether the menu is open. */
198
- get isOpen() {
199
- return this.#trigger !== undefined;
200
- }
201
-
202
- /**
203
- * Opens the menu.
204
- * @param {HTMLElement} trigger The element that triggered the menu.
205
- * @returns {Promise<void>} A `Promise` that resolves when the menu is opened.
206
- */
207
- async show(trigger: HTMLElement): Promise<void> {
208
- if (this.#trigger && this.#trigger !== trigger) {
209
- this.hide();
210
- }
211
-
212
- this.#anchoringCleanup?.();
213
- this.#anchoringCleanup = await positionAnchor(
214
- this,
215
- trigger,
216
- {
217
- position: M3eDirectionality.current === "ltr" ? "top-end" : "top-start",
218
- inline: true,
219
- shift: true,
220
- flip: true,
221
- offset: 8,
222
- },
223
- (x, y, position) => {
224
- this.classList.toggle("-right", position.includes("end"));
225
- this.classList.toggle("-left", position.includes("start"));
226
- this.style.left = `${x}px`;
227
- this.style.top = `${y}px`;
228
- }
229
- );
230
-
231
- this.showPopover();
232
-
233
- this.#trigger = trigger;
234
- this.#trigger.ariaExpanded = "true";
235
- this.#scrollController.observe(this.#trigger);
236
-
237
- this.#attachFab();
238
- }
239
-
240
- /**
241
- * Hides the menu.
242
- * @param {boolean} [restoreFocus=false] A value indicating whether to restore focus to the menu's trigger.
243
- */
244
- hide(restoreFocus: boolean = false): void {
245
- this.hidePopover();
246
-
247
- if (this.#trigger) {
248
- this.#trigger.ariaExpanded = "false";
249
- if (restoreFocus) {
250
- this.#trigger.focus();
251
- }
252
-
253
- this.#detachFab();
254
-
255
- this.#scrollController.unobserve(this.#trigger);
256
- this.#trigger = undefined;
257
- }
258
- }
259
-
260
- /**
261
- * Toggles the menu.
262
- * @param {HTMLElement} trigger The element that triggered the menu.
263
- * @returns {Promise<void>} A `Promise` that resolves when the menu is opened or closed.
264
- */
265
- async toggle(trigger: HTMLElement): Promise<void> {
266
- if (this.#trigger) {
267
- this.hide();
268
- } else {
269
- await this.show(trigger);
270
- }
271
- }
272
-
273
- /** @inheritdoc */
274
- override connectedCallback(): void {
275
- super.connectedCallback();
276
-
277
- this.tabIndex = -1;
278
- this.setAttribute("popover", "manual");
279
-
280
- this.addEventListener("keydown", this.#keyDownHandler);
281
- this.addEventListener("toggle", this.#toggleHandler);
282
- document.addEventListener("click", this.#documentClickHandler);
283
- }
284
-
285
- /** @inheritdoc */
286
- override disconnectedCallback(): void {
287
- super.disconnectedCallback();
288
-
289
- this.removeEventListener("keydown", this.#keyDownHandler);
290
- this.removeEventListener("toggle", this.#toggleHandler);
291
- document.removeEventListener("click", this.#documentClickHandler);
292
- }
293
-
294
- /** @inheritdoc */
295
- protected override render(): unknown {
296
- return html`<div class="base"><slot @slotchange="${this.#handleSlotChange}"></slot></div>`;
297
- }
298
-
299
- /** @private */
300
- #handleSlotChange(): void {
301
- const { added } = this.#listManager.setItems([...this.querySelectorAll("m3e-fab-menu-item")]);
302
- if (!this.#listManager.activeItem) {
303
- this.#listManager.updateActiveItem(added.find((x) => !x.disabled));
304
- }
305
- }
306
-
307
- /** @private */
308
- #handleKeyDown(e: KeyboardEvent): void {
309
- switch (e.key) {
310
- case "Tab":
311
- this.hide();
312
- break;
313
-
314
- case "Escape":
315
- if (!e.shiftKey && !e.ctrlKey) {
316
- this.hide(true);
317
- }
318
- break;
319
-
320
- default:
321
- this.#listManager.onKeyDown(e);
322
- break;
323
- }
324
- }
325
-
326
- /** @private */
327
- #handleDocumentClick(e: MouseEvent): void {
328
- if (!e.composedPath().some((x) => x instanceof M3eFabMenuItemElement || x === this.#trigger)) {
329
- this.hide();
330
- }
331
- }
332
-
333
- /** @private */
334
- #attachFab(): void {
335
- const fab = this.#trigger?.closest<M3eFabElement>("m3e-fab");
336
- if (fab) {
337
- this.#fabTabIndex = fab.tabIndex;
338
- fab.addEventListener("keydown", this.#keyDownHandler);
339
- this.#listManager.setItems([...this.#listManager.items, fab]);
340
- }
341
- }
342
-
343
- /** @private */
344
- #detachFab(): void {
345
- const fab = this.#trigger?.closest<M3eFabElement>("m3e-fab");
346
- if (fab) {
347
- if (this.#fabTabIndex !== undefined) {
348
- fab.tabIndex = this.#fabTabIndex;
349
- }
350
- fab.removeEventListener("keydown", this.#keyDownHandler);
351
- this.#listManager.setItems([...this.#listManager.items.filter((x) => x !== fab)]);
352
- }
353
- }
354
- }
355
-
356
- interface M3eFabMenuElementEventMap extends HTMLElementEventMap {
357
- beforetoggle: ToggleEvent;
358
- toggle: ToggleEvent;
359
- }
360
-
361
- export interface M3eFabMenuElement {
362
- addEventListener<K extends keyof M3eFabMenuElementEventMap>(
363
- type: K,
364
- listener: (this: M3eFabMenuElement, ev: M3eFabMenuElementEventMap[K]) => void,
365
- options?: boolean | AddEventListenerOptions
366
- ): void;
367
-
368
- addEventListener(
369
- type: string,
370
- listener: EventListenerOrEventListenerObject,
371
- options?: boolean | AddEventListenerOptions
372
- ): void;
373
-
374
- removeEventListener<K extends keyof M3eFabMenuElementEventMap>(
375
- type: K,
376
- listener: (this: M3eFabMenuElement, ev: M3eFabMenuElementEventMap[K]) => void,
377
- options?: boolean | EventListenerOptions
378
- ): void;
379
-
380
- removeEventListener(
381
- type: string,
382
- listener: EventListenerOrEventListenerObject,
383
- options?: boolean | EventListenerOptions
384
- ): void;
385
- }
386
-
387
- declare global {
388
- interface HTMLElementTagNameMap {
389
- "m3e-fab-menu": M3eFabMenuElement;
390
- }
391
- }