@api-client/ui 0.4.2 → 0.4.4
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.
- package/.vscode/settings.json +1 -0
- package/build/src/index.d.ts +3 -0
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +4 -0
- package/build/src/index.js.map +1 -1
- package/build/src/lib/Dom.d.ts +5 -0
- package/build/src/lib/Dom.d.ts.map +1 -0
- package/build/src/lib/Dom.js +40 -0
- package/build/src/lib/Dom.js.map +1 -0
- package/build/src/md/button/internals/base.d.ts +27 -0
- package/build/src/md/button/internals/base.d.ts.map +1 -1
- package/build/src/md/button/internals/base.js +90 -1
- package/build/src/md/button/internals/base.js.map +1 -1
- package/build/src/md/icons/internals/Icon.js +2 -2
- package/build/src/md/icons/internals/Icon.js.map +1 -1
- package/build/src/md/list/internals/List.d.ts +1 -1
- package/build/src/md/list/internals/List.d.ts.map +1 -1
- package/build/src/md/list/internals/List.js +4 -4
- package/build/src/md/list/internals/List.js.map +1 -1
- package/build/src/md/list/internals/ListItem.d.ts +1 -0
- package/build/src/md/list/internals/ListItem.d.ts.map +1 -1
- package/build/src/md/list/internals/ListItem.js +10 -10
- package/build/src/md/list/internals/ListItem.js.map +1 -1
- package/build/src/md/list/internals/ListItem.styles.d.ts.map +1 -1
- package/build/src/md/list/internals/ListItem.styles.js +2 -20
- package/build/src/md/list/internals/ListItem.styles.js.map +1 -1
- package/build/src/md/menu/index.d.ts +4 -0
- package/build/src/md/menu/index.d.ts.map +1 -0
- package/build/src/md/menu/index.js +4 -0
- package/build/src/md/menu/index.js.map +1 -0
- package/build/src/md/menu/internal/Menu.d.ts +76 -0
- package/build/src/md/menu/internal/Menu.d.ts.map +1 -0
- package/build/src/md/menu/internal/Menu.js +235 -0
- package/build/src/md/menu/internal/Menu.js.map +1 -0
- package/build/src/md/menu/internal/Menu.styles.d.ts +3 -0
- package/build/src/md/menu/internal/Menu.styles.d.ts.map +1 -0
- package/build/src/md/menu/internal/Menu.styles.js +185 -0
- package/build/src/md/menu/internal/Menu.styles.js.map +1 -0
- package/build/src/md/menu/internal/MenuItem.d.ts +77 -0
- package/build/src/md/menu/internal/MenuItem.d.ts.map +1 -0
- package/build/src/md/menu/internal/MenuItem.js +216 -0
- package/build/src/md/menu/internal/MenuItem.js.map +1 -0
- package/build/src/md/menu/internal/MenuItem.styles.d.ts +3 -0
- package/build/src/md/menu/internal/MenuItem.styles.d.ts.map +1 -0
- package/build/src/md/menu/internal/MenuItem.styles.js +64 -0
- package/build/src/md/menu/internal/MenuItem.styles.js.map +1 -0
- package/build/src/md/menu/internal/SubMenu.d.ts +56 -0
- package/build/src/md/menu/internal/SubMenu.d.ts.map +1 -0
- package/build/src/md/menu/internal/SubMenu.js +171 -0
- package/build/src/md/menu/internal/SubMenu.js.map +1 -0
- package/build/src/md/menu/internal/SubMenu.styles.d.ts +3 -0
- package/build/src/md/menu/internal/SubMenu.styles.d.ts.map +1 -0
- package/build/src/md/menu/internal/SubMenu.styles.js +8 -0
- package/build/src/md/menu/internal/SubMenu.styles.js.map +1 -0
- package/build/src/md/menu/ui-menu-item.d.ts +20 -0
- package/build/src/md/menu/ui-menu-item.d.ts.map +1 -0
- package/build/src/md/menu/ui-menu-item.js +37 -0
- package/build/src/md/menu/ui-menu-item.js.map +1 -0
- package/build/src/md/menu/ui-menu.d.ts +22 -0
- package/build/src/md/menu/ui-menu.d.ts.map +1 -0
- package/build/src/md/menu/ui-menu.js +38 -0
- package/build/src/md/menu/ui-menu.js.map +1 -0
- package/build/src/md/menu/ui-sub-menu.d.ts +20 -0
- package/build/src/md/menu/ui-sub-menu.d.ts.map +1 -0
- package/build/src/md/menu/ui-sub-menu.js +37 -0
- package/build/src/md/menu/ui-sub-menu.js.map +1 -0
- package/build/src/mixins/FileDropMixin.d.ts.map +1 -1
- package/build/src/mixins/FileDropMixin.js +7 -8
- package/build/src/mixins/FileDropMixin.js.map +1 -1
- package/build/src/mixins/RenderableMixin.d.ts.map +1 -1
- package/build/src/mixins/RenderableMixin.js +2 -3
- package/build/src/mixins/RenderableMixin.js.map +1 -1
- package/demo/md/index.html +2 -0
- package/demo/md/menu/index.html +19 -0
- package/demo/md/menu/index.ts +154 -0
- package/package.json +2 -3
- package/src/index.ts +5 -0
- package/src/lib/Dom.ts +41 -0
- package/src/md/button/internals/base.ts +77 -0
- package/src/md/icons/internals/Icon.ts +2 -2
- package/src/md/list/internals/List.ts +4 -4
- package/src/md/list/internals/ListItem.styles.ts +2 -20
- package/src/md/list/internals/ListItem.ts +11 -11
- package/src/md/menu/README.md +253 -0
- package/src/md/menu/index.ts +3 -0
- package/src/md/menu/internal/Menu.styles.ts +185 -0
- package/src/md/menu/internal/Menu.ts +205 -0
- package/src/md/menu/internal/MenuItem.styles.ts +64 -0
- package/src/md/menu/internal/MenuItem.ts +217 -0
- package/src/md/menu/internal/SubMenu.styles.ts +8 -0
- package/src/md/menu/internal/SubMenu.ts +179 -0
- package/src/md/menu/ui-menu-item.ts +25 -0
- package/src/md/menu/ui-menu.ts +26 -0
- package/src/md/menu/ui-sub-menu.ts +25 -0
- package/src/mixins/FileDropMixin.ts +106 -107
- package/src/mixins/RenderableMixin.ts +107 -108
- package/test/lib/Dom.test.ts +231 -0
- package/test/md/menu/Menu.test.ts +509 -0
- package/test/md/menu/MenuIntegration.test.ts +426 -0
- package/test/md/menu/MenuItem.test.ts +361 -0
- package/test/md/menu/SubMenu.test.ts +411 -0
- package/web-test-runner.config.js +1 -1
- /package/test/{ui → md}/button/UiButton.test.ts +0 -0
- /package/test/{ui → md}/button/UiIconButton.test.ts +0 -0
- /package/test/{ui → md}/chip/UiChip.test.ts +0 -0
- /package/test/{ui → md}/collapse/UiCollapse.test.ts +0 -0
- /package/test/{ui → md}/collapse/flex-layout.test.ts +0 -0
- /package/test/{ui → md}/date-time/DateTime.test.ts +0 -0
- /package/test/{ui → md}/dialog/UiDialog.test.ts +0 -0
- /package/test/{ui → md}/progress/UiProgressElement.test.ts +0 -0
- /package/test/{ui → md}/progress/UiRangeElement.test.ts +0 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export default css `
|
|
3
|
+
:host {
|
|
4
|
+
display: none;
|
|
5
|
+
position-area: bottom span-right;
|
|
6
|
+
position-try: normal flip-block;
|
|
7
|
+
position: absolute;
|
|
8
|
+
margin: 0;
|
|
9
|
+
padding: 0;
|
|
10
|
+
border: none;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
:host(:popover-open) {
|
|
14
|
+
display: block;
|
|
15
|
+
background-color: var(--md-sys-color-surface);
|
|
16
|
+
border-radius: var(--md-sys-shape-corner-extra-small);
|
|
17
|
+
box-shadow: var(--md-sys-elevation-3);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.menu-container {
|
|
21
|
+
min-width: 200px;
|
|
22
|
+
padding: 8px 0;
|
|
23
|
+
outline: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.menu-divider {
|
|
27
|
+
height: 1px;
|
|
28
|
+
background-color: var(--md-sys-color-outline-variant);
|
|
29
|
+
margin: 8px 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Menu Item Styles */
|
|
33
|
+
.menu-item {
|
|
34
|
+
position: relative;
|
|
35
|
+
display: flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
min-height: 48px;
|
|
38
|
+
padding: 0 16px;
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
outline: none;
|
|
41
|
+
transition: background-color 0.2s ease;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.menu-item:hover {
|
|
45
|
+
background-color: var(--md-sys-color-surface-variant);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.menu-item:focus {
|
|
49
|
+
background-color: var(--md-sys-color-surface-variant);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.menu-item[disabled] {
|
|
53
|
+
opacity: 0.38;
|
|
54
|
+
cursor: not-allowed;
|
|
55
|
+
pointer-events: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.menu-item-content {
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
width: 100%;
|
|
62
|
+
gap: 12px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.menu-item-icon {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
justify-content: center;
|
|
69
|
+
width: 24px;
|
|
70
|
+
height: 24px;
|
|
71
|
+
color: var(--md-sys-color-on-surface);
|
|
72
|
+
font-size: 20px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.menu-item-label {
|
|
76
|
+
flex: 1;
|
|
77
|
+
color: var(--md-sys-color-on-surface);
|
|
78
|
+
font-family: var(--md-sys-typescale-label-large-font-family-name);
|
|
79
|
+
font-size: var(--md-sys-typescale-label-large-font-size);
|
|
80
|
+
font-weight: var(--md-sys-typescale-label-large-font-weight);
|
|
81
|
+
line-height: var(--md-sys-typescale-label-large-line-height);
|
|
82
|
+
letter-spacing: var(--md-sys-typescale-label-large-letter-spacing);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.menu-item-arrow {
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
justify-content: center;
|
|
89
|
+
width: 24px;
|
|
90
|
+
height: 24px;
|
|
91
|
+
color: var(--md-sys-color-on-surface);
|
|
92
|
+
font-size: 18px;
|
|
93
|
+
font-weight: 500;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.menu-item-with-submenu {
|
|
97
|
+
position: relative;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.menu-item-with-submenu:hover .menu-item-arrow {
|
|
101
|
+
color: var(--md-sys-color-primary);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Sub-menu Styles */
|
|
105
|
+
.submenu-container {
|
|
106
|
+
min-width: 200px;
|
|
107
|
+
max-width: 320px;
|
|
108
|
+
background-color: var(--md-sys-color-surface);
|
|
109
|
+
border-radius: var(--md-sys-shape-corner-extra-small);
|
|
110
|
+
box-shadow: var(--md-sys-elevation-level3);
|
|
111
|
+
padding: 8px 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Submenu positioning with Anchor API */
|
|
115
|
+
ui-sub-menu {
|
|
116
|
+
display: none;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
ui-sub-menu:popover-open {
|
|
120
|
+
display: block;
|
|
121
|
+
background-color: var(--md-sys-color-surface);
|
|
122
|
+
border-radius: var(--md-sys-shape-corner-extra-small);
|
|
123
|
+
box-shadow: var(--md-sys-elevation-level3);
|
|
124
|
+
min-width: 200px;
|
|
125
|
+
max-width: 320px;
|
|
126
|
+
padding: 8px 0;
|
|
127
|
+
z-index: 1000;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Fallback positioning for browsers without anchor positioning */
|
|
131
|
+
@supports not (anchor-name: --test) {
|
|
132
|
+
ui-sub-menu:popover-open {
|
|
133
|
+
position: fixed;
|
|
134
|
+
transform: translateX(200px);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Focus Ring */
|
|
139
|
+
md-focus-ring {
|
|
140
|
+
--md-focus-ring-color: var(--md-sys-color-primary);
|
|
141
|
+
--md-focus-ring-width: 2px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Ripple Effect */
|
|
145
|
+
ui-ripple {
|
|
146
|
+
--md-ripple-color: var(--md-sys-color-primary);
|
|
147
|
+
--md-ripple-opacity: 0.12;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Responsive Design */
|
|
151
|
+
@media (max-width: 600px) {
|
|
152
|
+
.menu-container {
|
|
153
|
+
min-width: 180px;
|
|
154
|
+
max-width: 280px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.submenu-container {
|
|
158
|
+
min-width: 180px;
|
|
159
|
+
max-width: 280px;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* High Contrast Mode */
|
|
164
|
+
@media (prefers-contrast: high) {
|
|
165
|
+
.menu-container {
|
|
166
|
+
border: 1px solid var(--md-sys-color-outline);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.submenu-container {
|
|
170
|
+
border: 1px solid var(--md-sys-color-outline);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.menu-divider {
|
|
174
|
+
background-color: var(--md-sys-color-outline);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Reduced Motion */
|
|
179
|
+
@media (prefers-reduced-motion: reduce) {
|
|
180
|
+
.menu-item {
|
|
181
|
+
transition: none;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
`;
|
|
185
|
+
//# sourceMappingURL=Menu.styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Menu.styles.js","sourceRoot":"","sources":["../../../../../src/md/menu/internal/Menu.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,eAAe,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsLjB,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport default css`\n :host {\n display: none;\n position-area: bottom span-right;\n position-try: normal flip-block;\n position: absolute;\n margin: 0;\n padding: 0;\n border: none;\n }\n\n :host(:popover-open) {\n display: block;\n background-color: var(--md-sys-color-surface);\n border-radius: var(--md-sys-shape-corner-extra-small);\n box-shadow: var(--md-sys-elevation-3);\n }\n\n .menu-container {\n min-width: 200px;\n padding: 8px 0;\n outline: none;\n }\n\n .menu-divider {\n height: 1px;\n background-color: var(--md-sys-color-outline-variant);\n margin: 8px 0;\n }\n\n /* Menu Item Styles */\n .menu-item {\n position: relative;\n display: flex;\n align-items: center;\n min-height: 48px;\n padding: 0 16px;\n cursor: pointer;\n outline: none;\n transition: background-color 0.2s ease;\n }\n\n .menu-item:hover {\n background-color: var(--md-sys-color-surface-variant);\n }\n\n .menu-item:focus {\n background-color: var(--md-sys-color-surface-variant);\n }\n\n .menu-item[disabled] {\n opacity: 0.38;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .menu-item-content {\n display: flex;\n align-items: center;\n width: 100%;\n gap: 12px;\n }\n\n .menu-item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n color: var(--md-sys-color-on-surface);\n font-size: 20px;\n }\n\n .menu-item-label {\n flex: 1;\n color: var(--md-sys-color-on-surface);\n font-family: var(--md-sys-typescale-label-large-font-family-name);\n font-size: var(--md-sys-typescale-label-large-font-size);\n font-weight: var(--md-sys-typescale-label-large-font-weight);\n line-height: var(--md-sys-typescale-label-large-line-height);\n letter-spacing: var(--md-sys-typescale-label-large-letter-spacing);\n }\n\n .menu-item-arrow {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n color: var(--md-sys-color-on-surface);\n font-size: 18px;\n font-weight: 500;\n }\n\n .menu-item-with-submenu {\n position: relative;\n }\n\n .menu-item-with-submenu:hover .menu-item-arrow {\n color: var(--md-sys-color-primary);\n }\n\n /* Sub-menu Styles */\n .submenu-container {\n min-width: 200px;\n max-width: 320px;\n background-color: var(--md-sys-color-surface);\n border-radius: var(--md-sys-shape-corner-extra-small);\n box-shadow: var(--md-sys-elevation-level3);\n padding: 8px 0;\n }\n\n /* Submenu positioning with Anchor API */\n ui-sub-menu {\n display: none;\n }\n\n ui-sub-menu:popover-open {\n display: block;\n background-color: var(--md-sys-color-surface);\n border-radius: var(--md-sys-shape-corner-extra-small);\n box-shadow: var(--md-sys-elevation-level3);\n min-width: 200px;\n max-width: 320px;\n padding: 8px 0;\n z-index: 1000;\n }\n\n /* Fallback positioning for browsers without anchor positioning */\n @supports not (anchor-name: --test) {\n ui-sub-menu:popover-open {\n position: fixed;\n transform: translateX(200px);\n }\n }\n\n /* Focus Ring */\n md-focus-ring {\n --md-focus-ring-color: var(--md-sys-color-primary);\n --md-focus-ring-width: 2px;\n }\n\n /* Ripple Effect */\n ui-ripple {\n --md-ripple-color: var(--md-sys-color-primary);\n --md-ripple-opacity: 0.12;\n }\n\n /* Responsive Design */\n @media (max-width: 600px) {\n .menu-container {\n min-width: 180px;\n max-width: 280px;\n }\n\n .submenu-container {\n min-width: 180px;\n max-width: 280px;\n }\n }\n\n /* High Contrast Mode */\n @media (prefers-contrast: high) {\n .menu-container {\n border: 1px solid var(--md-sys-color-outline);\n }\n\n .submenu-container {\n border: 1px solid var(--md-sys-color-outline);\n }\n\n .menu-divider {\n background-color: var(--md-sys-color-outline);\n }\n }\n\n /* Reduced Motion */\n @media (prefers-reduced-motion: reduce) {\n .menu-item {\n transition: none;\n }\n }\n`\n"]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { PropertyValues, TemplateResult } from 'lit';
|
|
2
|
+
import UiListItem from '../../list/internals/ListItem.js';
|
|
3
|
+
import UiSubMenu from './SubMenu.js';
|
|
4
|
+
import '@material/web/focus/md-focus-ring.js';
|
|
5
|
+
import '../../ripple/ui-ripple.js';
|
|
6
|
+
/**
|
|
7
|
+
* Material Design 3 Menu Item component.
|
|
8
|
+
*
|
|
9
|
+
* @slot - The menu item content (label, icon, etc.)
|
|
10
|
+
* @fires select - Dispatched when the menu item is selected
|
|
11
|
+
* @fires submenu-open - Dispatched when a sub-menu is opened
|
|
12
|
+
*/
|
|
13
|
+
export default class UiMenuItem extends UiListItem {
|
|
14
|
+
/**
|
|
15
|
+
* The ID of the associated submenu
|
|
16
|
+
* @attribute
|
|
17
|
+
*/
|
|
18
|
+
accessor submenu: string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the menu item has a sub-menu
|
|
21
|
+
*/
|
|
22
|
+
get hasSubMenu(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Reference to the sub-menu element
|
|
25
|
+
*/
|
|
26
|
+
get subMenuElement(): UiSubMenu | null;
|
|
27
|
+
/**
|
|
28
|
+
* Whether the sub-menu is open
|
|
29
|
+
*/
|
|
30
|
+
protected accessor subMenuOpen: boolean;
|
|
31
|
+
constructor();
|
|
32
|
+
connectedCallback(): void;
|
|
33
|
+
protected updated(changedProperties: PropertyValues<this>): void;
|
|
34
|
+
/**
|
|
35
|
+
* Sets up the connection between this menu item and its submenu
|
|
36
|
+
*/
|
|
37
|
+
protected setupSubmenuConnection(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Updates accessibility attributes
|
|
40
|
+
*/
|
|
41
|
+
protected updateAccessibility(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Handles mouse enter events
|
|
44
|
+
*/
|
|
45
|
+
protected handleMouseEnter(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Handles mouse leave events
|
|
48
|
+
*/
|
|
49
|
+
protected handleMouseLeave(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Handles click events
|
|
52
|
+
*/
|
|
53
|
+
handleClick(e: MouseEvent): void;
|
|
54
|
+
/**
|
|
55
|
+
* Handles menu item click events
|
|
56
|
+
*/
|
|
57
|
+
protected handleMenuItemClick(e: MouseEvent): void;
|
|
58
|
+
/**
|
|
59
|
+
* Opens the sub-menu
|
|
60
|
+
*/
|
|
61
|
+
openSubMenu(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Closes the sub-menu
|
|
64
|
+
*/
|
|
65
|
+
closeSubMenu(): void;
|
|
66
|
+
/**
|
|
67
|
+
* Toggles the sub-menu
|
|
68
|
+
*/
|
|
69
|
+
toggleSubMenu(): void;
|
|
70
|
+
/**
|
|
71
|
+
* Handles sub-menu item selection
|
|
72
|
+
*/
|
|
73
|
+
protected handleSubMenuSelect(e: CustomEvent): void;
|
|
74
|
+
render(): TemplateResult;
|
|
75
|
+
protected renderEnd(): TemplateResult;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=MenuItem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MenuItem.d.ts","sourceRoot":"","sources":["../../../../../src/md/menu/internal/MenuItem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,cAAc,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAG1D,OAAO,UAAU,MAAM,kCAAkC,CAAA;AACzD,OAAO,SAAS,MAAM,cAAc,CAAA;AAIpC,OAAO,sCAAsC,CAAA;AAC7C,OAAO,2BAA2B,CAAA;AAElC;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,UAAU;IAChD;;;OAGG;IACyB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAEhE;;OAEG;IACH,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;OAEG;IACH,IAAI,cAAc,IAAI,SAAS,GAAG,IAAI,CAGrC;IAED;;OAEG;IACM,SAAS,CAAC,QAAQ,CAAC,WAAW,UAAQ;;IAStC,iBAAiB,IAAI,IAAI;cAUf,OAAO,CAAC,iBAAiB,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI;IASzE;;OAEG;IACH,SAAS,CAAC,sBAAsB,IAAI,IAAI;IAYxC;;OAEG;IACH,SAAS,CAAC,mBAAmB,IAAI,IAAI;IAUrC;;OAEG;IACH,SAAS,CAAC,gBAAgB,IAAI,IAAI;IAMlC;;OAEG;IACH,SAAS,CAAC,gBAAgB,IAAI,IAAI;IASlC;;OAEG;IACa,WAAW,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI;IAUhD;;OAEG;IACH,SAAS,CAAC,mBAAmB,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI;IAIlD;;OAEG;IACH,WAAW,IAAI,IAAI;IAgBnB;;OAEG;IACH,YAAY,IAAI,IAAI;IAQpB;;OAEG;IACH,aAAa,IAAI,IAAI;IAQrB;;OAEG;IACH,SAAS,CAAC,mBAAmB,CAAC,CAAC,EAAE,WAAW,GAAG,IAAI;IAW1C,MAAM,IAAI,cAAc;cAcd,SAAS,IAAI,cAAc;CAO/C"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { __esDecorate, __runInitializers } from "tslib";
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { property, state } from 'lit/decorators.js';
|
|
4
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
5
|
+
import UiListItem from '../../list/internals/ListItem.js';
|
|
6
|
+
import { findElementInShadowRoots } from '../../../lib/Dom.js';
|
|
7
|
+
import { nanoid } from 'nanoid';
|
|
8
|
+
import '@material/web/focus/md-focus-ring.js';
|
|
9
|
+
import '../../ripple/ui-ripple.js';
|
|
10
|
+
let UiMenuItem = (() => {
|
|
11
|
+
let _classSuper = UiListItem;
|
|
12
|
+
let _submenu_decorators;
|
|
13
|
+
let _submenu_initializers = [];
|
|
14
|
+
let _submenu_extraInitializers = [];
|
|
15
|
+
let _subMenuOpen_decorators;
|
|
16
|
+
let _subMenuOpen_initializers = [];
|
|
17
|
+
let _subMenuOpen_extraInitializers = [];
|
|
18
|
+
return class UiMenuItem extends _classSuper {
|
|
19
|
+
static {
|
|
20
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
21
|
+
_submenu_decorators = [property({ type: String })];
|
|
22
|
+
_subMenuOpen_decorators = [state()];
|
|
23
|
+
__esDecorate(this, null, _submenu_decorators, { kind: "accessor", name: "submenu", static: false, private: false, access: { has: obj => "submenu" in obj, get: obj => obj.submenu, set: (obj, value) => { obj.submenu = value; } }, metadata: _metadata }, _submenu_initializers, _submenu_extraInitializers);
|
|
24
|
+
__esDecorate(this, null, _subMenuOpen_decorators, { kind: "accessor", name: "subMenuOpen", static: false, private: false, access: { has: obj => "subMenuOpen" in obj, get: obj => obj.subMenuOpen, set: (obj, value) => { obj.subMenuOpen = value; } }, metadata: _metadata }, _subMenuOpen_initializers, _subMenuOpen_extraInitializers);
|
|
25
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
26
|
+
}
|
|
27
|
+
#submenu_accessor_storage = __runInitializers(this, _submenu_initializers, void 0);
|
|
28
|
+
/**
|
|
29
|
+
* The ID of the associated submenu
|
|
30
|
+
* @attribute
|
|
31
|
+
*/
|
|
32
|
+
get submenu() { return this.#submenu_accessor_storage; }
|
|
33
|
+
set submenu(value) { this.#submenu_accessor_storage = value; }
|
|
34
|
+
/**
|
|
35
|
+
* Whether the menu item has a sub-menu
|
|
36
|
+
*/
|
|
37
|
+
get hasSubMenu() {
|
|
38
|
+
return !!this.submenu && !!this.subMenuElement;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reference to the sub-menu element
|
|
42
|
+
*/
|
|
43
|
+
get subMenuElement() {
|
|
44
|
+
if (!this.submenu)
|
|
45
|
+
return null;
|
|
46
|
+
return findElementInShadowRoots(this.submenu, this);
|
|
47
|
+
}
|
|
48
|
+
#subMenuOpen_accessor_storage = (__runInitializers(this, _submenu_extraInitializers), __runInitializers(this, _subMenuOpen_initializers, false));
|
|
49
|
+
/**
|
|
50
|
+
* Whether the sub-menu is open
|
|
51
|
+
*/
|
|
52
|
+
get subMenuOpen() { return this.#subMenuOpen_accessor_storage; }
|
|
53
|
+
set subMenuOpen(value) { this.#subMenuOpen_accessor_storage = value; }
|
|
54
|
+
constructor() {
|
|
55
|
+
super();
|
|
56
|
+
__runInitializers(this, _subMenuOpen_extraInitializers);
|
|
57
|
+
this.addEventListener('mouseenter', this.handleMouseEnter.bind(this));
|
|
58
|
+
this.addEventListener('mouseleave', this.handleMouseLeave.bind(this));
|
|
59
|
+
this.addEventListener('click', this.handleMenuItemClick.bind(this));
|
|
60
|
+
}
|
|
61
|
+
connectedCallback() {
|
|
62
|
+
super.connectedCallback();
|
|
63
|
+
this.setAttribute('role', 'menuitem');
|
|
64
|
+
// Generate ID if not present (needed for submenu anchoring)
|
|
65
|
+
if (!this.id) {
|
|
66
|
+
this.id = nanoid(6);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
updated(changedProperties) {
|
|
70
|
+
super.updated(changedProperties);
|
|
71
|
+
if (changedProperties.has('submenu')) {
|
|
72
|
+
this.updateAccessibility();
|
|
73
|
+
this.setupSubmenuConnection();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Sets up the connection between this menu item and its submenu
|
|
78
|
+
*/
|
|
79
|
+
setupSubmenuConnection() {
|
|
80
|
+
if (this.subMenuElement) {
|
|
81
|
+
this.subMenuElement.anchor = this.id;
|
|
82
|
+
// Find parent menu and set it on the submenu
|
|
83
|
+
const parentMenu = this.closest('ui-menu, ui-sub-menu');
|
|
84
|
+
if (parentMenu) {
|
|
85
|
+
this.subMenuElement.setParentMenu(parentMenu);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Updates accessibility attributes
|
|
91
|
+
*/
|
|
92
|
+
updateAccessibility() {
|
|
93
|
+
if (this.hasSubMenu) {
|
|
94
|
+
this.setAttribute('aria-haspopup', 'true');
|
|
95
|
+
this.setAttribute('aria-expanded', String(this.subMenuOpen));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.removeAttribute('aria-haspopup');
|
|
99
|
+
this.removeAttribute('aria-expanded');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Handles mouse enter events
|
|
104
|
+
*/
|
|
105
|
+
handleMouseEnter() {
|
|
106
|
+
if (this.hasSubMenu && !this.subMenuOpen) {
|
|
107
|
+
this.openSubMenu();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Handles mouse leave events
|
|
112
|
+
*/
|
|
113
|
+
handleMouseLeave() {
|
|
114
|
+
// Close sub-menu after a delay to allow moving to sub-menu
|
|
115
|
+
setTimeout(() => {
|
|
116
|
+
if (this.subMenuOpen && !this.matches(':hover') && !this.subMenuElement?.matches(':hover')) {
|
|
117
|
+
this.closeSubMenu();
|
|
118
|
+
}
|
|
119
|
+
}, 100);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Handles click events
|
|
123
|
+
*/
|
|
124
|
+
handleClick(e) {
|
|
125
|
+
if (this.hasSubMenu) {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
e.stopPropagation();
|
|
128
|
+
this.toggleSubMenu();
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
super.handleClick(e);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Handles menu item click events
|
|
136
|
+
*/
|
|
137
|
+
handleMenuItemClick(e) {
|
|
138
|
+
this.handleClick(e);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Opens the sub-menu
|
|
142
|
+
*/
|
|
143
|
+
openSubMenu() {
|
|
144
|
+
if (!this.hasSubMenu || this.subMenuOpen)
|
|
145
|
+
return;
|
|
146
|
+
this.subMenuOpen = true;
|
|
147
|
+
this.updateAccessibility();
|
|
148
|
+
this.subMenuElement?.show();
|
|
149
|
+
this.dispatchEvent(new CustomEvent('submenu-open', {
|
|
150
|
+
detail: { subMenu: this.subMenuElement },
|
|
151
|
+
bubbles: true,
|
|
152
|
+
composed: true,
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Closes the sub-menu
|
|
157
|
+
*/
|
|
158
|
+
closeSubMenu() {
|
|
159
|
+
if (!this.subMenuOpen)
|
|
160
|
+
return;
|
|
161
|
+
this.subMenuOpen = false;
|
|
162
|
+
this.updateAccessibility();
|
|
163
|
+
this.subMenuElement?.hide();
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Toggles the sub-menu
|
|
167
|
+
*/
|
|
168
|
+
toggleSubMenu() {
|
|
169
|
+
if (this.subMenuOpen) {
|
|
170
|
+
this.closeSubMenu();
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
this.openSubMenu();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Handles sub-menu item selection
|
|
178
|
+
*/
|
|
179
|
+
handleSubMenuSelect(e) {
|
|
180
|
+
// Bubble up the selection event
|
|
181
|
+
this.dispatchEvent(new CustomEvent('select', {
|
|
182
|
+
detail: e.detail,
|
|
183
|
+
bubbles: true,
|
|
184
|
+
composed: true,
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
render() {
|
|
188
|
+
const classes = classMap({
|
|
189
|
+
'surface': true,
|
|
190
|
+
'menu-item': true,
|
|
191
|
+
'menu-item-with-submenu': this.hasSubMenu,
|
|
192
|
+
'submenu-open': this.subMenuOpen,
|
|
193
|
+
});
|
|
194
|
+
return html `
|
|
195
|
+
${this.renderFocusRing()} ${this.renderRipple()}
|
|
196
|
+
<div class=${classes} role="menuitem">${this.renderStart()} ${this.renderBody()} ${this.renderEnd()}</div>
|
|
197
|
+
`;
|
|
198
|
+
}
|
|
199
|
+
renderEnd() {
|
|
200
|
+
return html `<div class="end">
|
|
201
|
+
<slot name="end" @slotchange=${this.handleSlotChange}></slot>
|
|
202
|
+
<span class="trailing-supporting-text"><slot name="end-text"></slot></span>
|
|
203
|
+
${this.hasSubMenu ? html `<ui-icon class="menu-item-arrow">arrow_right</ui-icon>` : ''}
|
|
204
|
+
</div>`;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
})();
|
|
208
|
+
/**
|
|
209
|
+
* Material Design 3 Menu Item component.
|
|
210
|
+
*
|
|
211
|
+
* @slot - The menu item content (label, icon, etc.)
|
|
212
|
+
* @fires select - Dispatched when the menu item is selected
|
|
213
|
+
* @fires submenu-open - Dispatched when a sub-menu is opened
|
|
214
|
+
*/
|
|
215
|
+
export default UiMenuItem;
|
|
216
|
+
//# sourceMappingURL=MenuItem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MenuItem.js","sourceRoot":"","sources":["../../../../../src/md/menu/internal/MenuItem.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, PropertyValues, TemplateResult } from 'lit'\nimport { property, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport UiListItem from '../../list/internals/ListItem.js'\nimport UiSubMenu from './SubMenu.js'\nimport { findElementInShadowRoots } from '../../../lib/Dom.js'\nimport { nanoid } from 'nanoid'\n\nimport '@material/web/focus/md-focus-ring.js'\nimport '../../ripple/ui-ripple.js'\n\n/**\n * Material Design 3 Menu Item component.\n *\n * @slot - The menu item content (label, icon, etc.)\n * @fires select - Dispatched when the menu item is selected\n * @fires submenu-open - Dispatched when a sub-menu is opened\n */\nexport default class UiMenuItem extends UiListItem {\n /**\n * The ID of the associated submenu\n * @attribute\n */\n @property({ type: String }) accessor submenu: string | undefined\n\n /**\n * Whether the menu item has a sub-menu\n */\n get hasSubMenu(): boolean {\n return !!this.submenu && !!this.subMenuElement\n }\n\n /**\n * Reference to the sub-menu element\n */\n get subMenuElement(): UiSubMenu | null {\n if (!this.submenu) return null\n return findElementInShadowRoots(this.submenu, this) as UiSubMenu | null\n }\n\n /**\n * Whether the sub-menu is open\n */\n @state() protected accessor subMenuOpen = false\n\n constructor() {\n super()\n this.addEventListener('mouseenter', this.handleMouseEnter.bind(this))\n this.addEventListener('mouseleave', this.handleMouseLeave.bind(this))\n this.addEventListener('click', this.handleMenuItemClick.bind(this))\n }\n\n override connectedCallback(): void {\n super.connectedCallback()\n this.setAttribute('role', 'menuitem')\n\n // Generate ID if not present (needed for submenu anchoring)\n if (!this.id) {\n this.id = nanoid(6)\n }\n }\n\n protected override updated(changedProperties: PropertyValues<this>): void {\n super.updated(changedProperties)\n\n if (changedProperties.has('submenu')) {\n this.updateAccessibility()\n this.setupSubmenuConnection()\n }\n }\n\n /**\n * Sets up the connection between this menu item and its submenu\n */\n protected setupSubmenuConnection(): void {\n if (this.subMenuElement) {\n this.subMenuElement.anchor = this.id\n\n // Find parent menu and set it on the submenu\n const parentMenu = this.closest('ui-menu, ui-sub-menu') as UiSubMenu\n if (parentMenu) {\n this.subMenuElement.setParentMenu(parentMenu)\n }\n }\n }\n\n /**\n * Updates accessibility attributes\n */\n protected updateAccessibility(): void {\n if (this.hasSubMenu) {\n this.setAttribute('aria-haspopup', 'true')\n this.setAttribute('aria-expanded', String(this.subMenuOpen))\n } else {\n this.removeAttribute('aria-haspopup')\n this.removeAttribute('aria-expanded')\n }\n }\n\n /**\n * Handles mouse enter events\n */\n protected handleMouseEnter(): void {\n if (this.hasSubMenu && !this.subMenuOpen) {\n this.openSubMenu()\n }\n }\n\n /**\n * Handles mouse leave events\n */\n protected handleMouseLeave(): void {\n // Close sub-menu after a delay to allow moving to sub-menu\n setTimeout(() => {\n if (this.subMenuOpen && !this.matches(':hover') && !this.subMenuElement?.matches(':hover')) {\n this.closeSubMenu()\n }\n }, 100)\n }\n\n /**\n * Handles click events\n */\n public override handleClick(e: MouseEvent): void {\n if (this.hasSubMenu) {\n e.preventDefault()\n e.stopPropagation()\n this.toggleSubMenu()\n } else {\n super.handleClick(e)\n }\n }\n\n /**\n * Handles menu item click events\n */\n protected handleMenuItemClick(e: MouseEvent): void {\n this.handleClick(e)\n }\n\n /**\n * Opens the sub-menu\n */\n openSubMenu(): void {\n if (!this.hasSubMenu || this.subMenuOpen) return\n\n this.subMenuOpen = true\n this.updateAccessibility()\n this.subMenuElement?.show()\n\n this.dispatchEvent(\n new CustomEvent('submenu-open', {\n detail: { subMenu: this.subMenuElement },\n bubbles: true,\n composed: true,\n })\n )\n }\n\n /**\n * Closes the sub-menu\n */\n closeSubMenu(): void {\n if (!this.subMenuOpen) return\n\n this.subMenuOpen = false\n this.updateAccessibility()\n this.subMenuElement?.hide()\n }\n\n /**\n * Toggles the sub-menu\n */\n toggleSubMenu(): void {\n if (this.subMenuOpen) {\n this.closeSubMenu()\n } else {\n this.openSubMenu()\n }\n }\n\n /**\n * Handles sub-menu item selection\n */\n protected handleSubMenuSelect(e: CustomEvent): void {\n // Bubble up the selection event\n this.dispatchEvent(\n new CustomEvent('select', {\n detail: e.detail,\n bubbles: true,\n composed: true,\n })\n )\n }\n\n override render(): TemplateResult {\n const classes = classMap({\n 'surface': true,\n 'menu-item': true,\n 'menu-item-with-submenu': this.hasSubMenu,\n 'submenu-open': this.subMenuOpen,\n })\n\n return html`\n ${this.renderFocusRing()} ${this.renderRipple()}\n <div class=${classes} role=\"menuitem\">${this.renderStart()} ${this.renderBody()} ${this.renderEnd()}</div>\n `\n }\n\n protected override renderEnd(): TemplateResult {\n return html`<div class=\"end\">\n <slot name=\"end\" @slotchange=${this.handleSlotChange}></slot>\n <span class=\"trailing-supporting-text\"><slot name=\"end-text\"></slot></span>\n ${this.hasSubMenu ? html`<ui-icon class=\"menu-item-arrow\">arrow_right</ui-icon>` : ''}\n </div>`\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MenuItem.styles.d.ts","sourceRoot":"","sources":["../../../../../src/md/menu/internal/MenuItem.styles.ts"],"names":[],"mappings":";AAEA,wBA6DC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export default css `
|
|
3
|
+
:host {
|
|
4
|
+
display: block;
|
|
5
|
+
position: relative;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.menu-item {
|
|
9
|
+
position: relative;
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
min-height: 48px;
|
|
13
|
+
padding: 0 16px;
|
|
14
|
+
cursor: pointer;
|
|
15
|
+
outline: none;
|
|
16
|
+
transition: background-color 0.2s ease;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.menu-item:hover {
|
|
20
|
+
background-color: var(--md-sys-color-surface-variant);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.menu-item:focus {
|
|
24
|
+
background-color: var(--md-sys-color-surface-variant);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.menu-item[disabled] {
|
|
28
|
+
opacity: 0.38;
|
|
29
|
+
cursor: not-allowed;
|
|
30
|
+
pointer-events: none;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.menu-item-with-submenu {
|
|
34
|
+
position: relative;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.menu-item-with-submenu:hover .menu-item-arrow {
|
|
38
|
+
color: var(--md-sys-color-primary);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.menu-item-arrow {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
width: 24px;
|
|
46
|
+
height: 24px;
|
|
47
|
+
color: var(--md-sys-color-on-surface);
|
|
48
|
+
font-size: 18px;
|
|
49
|
+
font-weight: 500;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Focus Ring */
|
|
53
|
+
md-focus-ring {
|
|
54
|
+
--md-focus-ring-color: var(--md-sys-color-primary);
|
|
55
|
+
--md-focus-ring-width: 2px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Ripple Effect */
|
|
59
|
+
ui-ripple {
|
|
60
|
+
--md-ripple-color: var(--md-sys-color-primary);
|
|
61
|
+
--md-ripple-opacity: 0.12;
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
//# sourceMappingURL=MenuItem.styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MenuItem.styles.js","sourceRoot":"","sources":["../../../../../src/md/menu/internal/MenuItem.styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,eAAe,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6DjB,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport default css`\n :host {\n display: block;\n position: relative;\n }\n\n .menu-item {\n position: relative;\n display: flex;\n align-items: center;\n min-height: 48px;\n padding: 0 16px;\n cursor: pointer;\n outline: none;\n transition: background-color 0.2s ease;\n }\n\n .menu-item:hover {\n background-color: var(--md-sys-color-surface-variant);\n }\n\n .menu-item:focus {\n background-color: var(--md-sys-color-surface-variant);\n }\n\n .menu-item[disabled] {\n opacity: 0.38;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n .menu-item-with-submenu {\n position: relative;\n }\n\n .menu-item-with-submenu:hover .menu-item-arrow {\n color: var(--md-sys-color-primary);\n }\n\n .menu-item-arrow {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n color: var(--md-sys-color-on-surface);\n font-size: 18px;\n font-weight: 500;\n }\n\n /* Focus Ring */\n md-focus-ring {\n --md-focus-ring-color: var(--md-sys-color-primary);\n --md-focus-ring-width: 2px;\n }\n\n /* Ripple Effect */\n ui-ripple {\n --md-ripple-color: var(--md-sys-color-primary);\n --md-ripple-opacity: 0.12;\n }\n`\n"]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { PropertyValues, TemplateResult } from 'lit';
|
|
2
|
+
import Menu from './Menu.js';
|
|
3
|
+
import UiListItem from '../../list/internals/ListItem.js';
|
|
4
|
+
import type { MenuItem } from '../../../index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Material Design 3 Sub-Menu component.
|
|
7
|
+
* Extends the main Menu component to provide sub-menu functionality.
|
|
8
|
+
* Uses Popover API and Anchor Positioning API for modern positioning.
|
|
9
|
+
*
|
|
10
|
+
* @slot - The sub-menu items
|
|
11
|
+
* @fires select - Dispatched when a sub-menu item is selected
|
|
12
|
+
* @fires close - Dispatched when the sub-menu is closed
|
|
13
|
+
*/
|
|
14
|
+
export default class UiSubMenu extends Menu {
|
|
15
|
+
/**
|
|
16
|
+
* The ID of the anchor element (parent menu item)
|
|
17
|
+
* @attribute
|
|
18
|
+
*/
|
|
19
|
+
accessor anchor: string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Reference to the parent menu
|
|
22
|
+
*/
|
|
23
|
+
parentMenu: Menu | null;
|
|
24
|
+
/**
|
|
25
|
+
* Reference to the anchor element
|
|
26
|
+
*/
|
|
27
|
+
get anchorElement(): MenuItem | null;
|
|
28
|
+
connectedCallback(): void;
|
|
29
|
+
protected updated(changedProperties: PropertyValues<this>): void;
|
|
30
|
+
/**
|
|
31
|
+
* Updates anchor positioning using CSS Anchor Positioning API
|
|
32
|
+
*/
|
|
33
|
+
protected updateAnchorPositioning(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Shows the submenu
|
|
36
|
+
*/
|
|
37
|
+
show(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Hides the submenu
|
|
40
|
+
*/
|
|
41
|
+
hide(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Sets the parent menu reference
|
|
44
|
+
*/
|
|
45
|
+
setParentMenu(menu: Menu): void;
|
|
46
|
+
/**
|
|
47
|
+
* Handles selection events - bubbles them up to parent menu
|
|
48
|
+
*/
|
|
49
|
+
notifySelect(item: UiListItem): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Handles keyboard navigation specific to submenus
|
|
52
|
+
*/
|
|
53
|
+
handleKeydown(e: KeyboardEvent): void;
|
|
54
|
+
render(): TemplateResult;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=SubMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SubMenu.d.ts","sourceRoot":"","sources":["../../../../../src/md/menu/internal/SubMenu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,cAAc,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAG1D,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,UAAU,MAAM,kCAAkC,CAAA;AAEzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAEjD;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,IAAI;IACzC;;;OAGG;IACyB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAE/D;;OAEG;IACH,UAAU,EAAE,IAAI,GAAG,IAAI,CAAO;IAE9B;;OAEG;IACH,IAAI,aAAa,IAAI,QAAQ,GAAG,IAAI,CAGnC;IAEQ,iBAAiB,IAAI,IAAI;cAMf,OAAO,CAAC,iBAAiB,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI;IAQzE;;OAEG;IACH,SAAS,CAAC,uBAAuB,IAAI,IAAI;IAYzC;;OAEG;IACM,IAAI,IAAI,IAAI;IA4BrB;;OAEG;IACM,IAAI,IAAI,IAAI;IAcrB;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAI/B;;OAEG;IACM,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;IAahD;;OAEG;IACM,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI;IA0BrC,MAAM,IAAI,cAAc;CAYlC"}
|