@api-client/ui 0.4.2 → 0.4.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.
- 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 +24 -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 +26 -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/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/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,217 @@
|
|
|
1
|
+
import { html, PropertyValues, TemplateResult } from 'lit'
|
|
2
|
+
import { property, state } from 'lit/decorators.js'
|
|
3
|
+
import { classMap } from 'lit/directives/class-map.js'
|
|
4
|
+
import UiListItem from '../../list/internals/ListItem.js'
|
|
5
|
+
import UiSubMenu from './SubMenu.js'
|
|
6
|
+
import { findElementInShadowRoots } from '../../../lib/Dom.js'
|
|
7
|
+
import { nanoid } from 'nanoid'
|
|
8
|
+
|
|
9
|
+
import '@material/web/focus/md-focus-ring.js'
|
|
10
|
+
import '../../ripple/ui-ripple.js'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Material Design 3 Menu Item component.
|
|
14
|
+
*
|
|
15
|
+
* @slot - The menu item content (label, icon, etc.)
|
|
16
|
+
* @fires select - Dispatched when the menu item is selected
|
|
17
|
+
* @fires submenu-open - Dispatched when a sub-menu is opened
|
|
18
|
+
*/
|
|
19
|
+
export default class UiMenuItem extends UiListItem {
|
|
20
|
+
/**
|
|
21
|
+
* The ID of the associated submenu
|
|
22
|
+
* @attribute
|
|
23
|
+
*/
|
|
24
|
+
@property({ type: String }) accessor submenu: string | undefined
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Whether the menu item has a sub-menu
|
|
28
|
+
*/
|
|
29
|
+
get hasSubMenu(): boolean {
|
|
30
|
+
return !!this.submenu && !!this.subMenuElement
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Reference to the sub-menu element
|
|
35
|
+
*/
|
|
36
|
+
get subMenuElement(): UiSubMenu | null {
|
|
37
|
+
if (!this.submenu) return null
|
|
38
|
+
return findElementInShadowRoots(this.submenu, this) as UiSubMenu | null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Whether the sub-menu is open
|
|
43
|
+
*/
|
|
44
|
+
@state() protected accessor subMenuOpen = false
|
|
45
|
+
|
|
46
|
+
constructor() {
|
|
47
|
+
super()
|
|
48
|
+
this.addEventListener('mouseenter', this.handleMouseEnter.bind(this))
|
|
49
|
+
this.addEventListener('mouseleave', this.handleMouseLeave.bind(this))
|
|
50
|
+
this.addEventListener('click', this.handleMenuItemClick.bind(this))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
override connectedCallback(): void {
|
|
54
|
+
super.connectedCallback()
|
|
55
|
+
this.setAttribute('role', 'menuitem')
|
|
56
|
+
|
|
57
|
+
// Generate ID if not present (needed for submenu anchoring)
|
|
58
|
+
if (!this.id) {
|
|
59
|
+
this.id = nanoid(6)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected override updated(changedProperties: PropertyValues<this>): void {
|
|
64
|
+
super.updated(changedProperties)
|
|
65
|
+
|
|
66
|
+
if (changedProperties.has('submenu')) {
|
|
67
|
+
this.updateAccessibility()
|
|
68
|
+
this.setupSubmenuConnection()
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Sets up the connection between this menu item and its submenu
|
|
74
|
+
*/
|
|
75
|
+
protected setupSubmenuConnection(): void {
|
|
76
|
+
if (this.subMenuElement) {
|
|
77
|
+
this.subMenuElement.anchor = this.id
|
|
78
|
+
|
|
79
|
+
// Find parent menu and set it on the submenu
|
|
80
|
+
const parentMenu = this.closest('ui-menu, ui-sub-menu') as UiSubMenu
|
|
81
|
+
if (parentMenu) {
|
|
82
|
+
this.subMenuElement.setParentMenu(parentMenu)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Updates accessibility attributes
|
|
89
|
+
*/
|
|
90
|
+
protected updateAccessibility(): void {
|
|
91
|
+
if (this.hasSubMenu) {
|
|
92
|
+
this.setAttribute('aria-haspopup', 'true')
|
|
93
|
+
this.setAttribute('aria-expanded', String(this.subMenuOpen))
|
|
94
|
+
} else {
|
|
95
|
+
this.removeAttribute('aria-haspopup')
|
|
96
|
+
this.removeAttribute('aria-expanded')
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Handles mouse enter events
|
|
102
|
+
*/
|
|
103
|
+
protected handleMouseEnter(): void {
|
|
104
|
+
if (this.hasSubMenu && !this.subMenuOpen) {
|
|
105
|
+
this.openSubMenu()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Handles mouse leave events
|
|
111
|
+
*/
|
|
112
|
+
protected handleMouseLeave(): void {
|
|
113
|
+
// Close sub-menu after a delay to allow moving to sub-menu
|
|
114
|
+
setTimeout(() => {
|
|
115
|
+
if (this.subMenuOpen && !this.matches(':hover') && !this.subMenuElement?.matches(':hover')) {
|
|
116
|
+
this.closeSubMenu()
|
|
117
|
+
}
|
|
118
|
+
}, 100)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Handles click events
|
|
123
|
+
*/
|
|
124
|
+
public override handleClick(e: MouseEvent): void {
|
|
125
|
+
if (this.hasSubMenu) {
|
|
126
|
+
e.preventDefault()
|
|
127
|
+
e.stopPropagation()
|
|
128
|
+
this.toggleSubMenu()
|
|
129
|
+
} else {
|
|
130
|
+
super.handleClick(e)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Handles menu item click events
|
|
136
|
+
*/
|
|
137
|
+
protected handleMenuItemClick(e: MouseEvent): void {
|
|
138
|
+
this.handleClick(e)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Opens the sub-menu
|
|
143
|
+
*/
|
|
144
|
+
openSubMenu(): void {
|
|
145
|
+
if (!this.hasSubMenu || this.subMenuOpen) return
|
|
146
|
+
|
|
147
|
+
this.subMenuOpen = true
|
|
148
|
+
this.updateAccessibility()
|
|
149
|
+
this.subMenuElement?.show()
|
|
150
|
+
|
|
151
|
+
this.dispatchEvent(
|
|
152
|
+
new CustomEvent('submenu-open', {
|
|
153
|
+
detail: { subMenu: this.subMenuElement },
|
|
154
|
+
bubbles: true,
|
|
155
|
+
composed: true,
|
|
156
|
+
})
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Closes the sub-menu
|
|
162
|
+
*/
|
|
163
|
+
closeSubMenu(): void {
|
|
164
|
+
if (!this.subMenuOpen) return
|
|
165
|
+
|
|
166
|
+
this.subMenuOpen = false
|
|
167
|
+
this.updateAccessibility()
|
|
168
|
+
this.subMenuElement?.hide()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Toggles the sub-menu
|
|
173
|
+
*/
|
|
174
|
+
toggleSubMenu(): void {
|
|
175
|
+
if (this.subMenuOpen) {
|
|
176
|
+
this.closeSubMenu()
|
|
177
|
+
} else {
|
|
178
|
+
this.openSubMenu()
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Handles sub-menu item selection
|
|
184
|
+
*/
|
|
185
|
+
protected handleSubMenuSelect(e: CustomEvent): void {
|
|
186
|
+
// Bubble up the selection event
|
|
187
|
+
this.dispatchEvent(
|
|
188
|
+
new CustomEvent('select', {
|
|
189
|
+
detail: e.detail,
|
|
190
|
+
bubbles: true,
|
|
191
|
+
composed: true,
|
|
192
|
+
})
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
override render(): TemplateResult {
|
|
197
|
+
const classes = classMap({
|
|
198
|
+
'surface': true,
|
|
199
|
+
'menu-item': true,
|
|
200
|
+
'menu-item-with-submenu': this.hasSubMenu,
|
|
201
|
+
'submenu-open': this.subMenuOpen,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
return html`
|
|
205
|
+
${this.renderFocusRing()} ${this.renderRipple()}
|
|
206
|
+
<div class=${classes} role="menuitem">${this.renderStart()} ${this.renderBody()} ${this.renderEnd()}</div>
|
|
207
|
+
`
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
protected override renderEnd(): TemplateResult {
|
|
211
|
+
return html`<div class="end">
|
|
212
|
+
<slot name="end" @slotchange=${this.handleSlotChange}></slot>
|
|
213
|
+
<span class="trailing-supporting-text"><slot name="end-text"></slot></span>
|
|
214
|
+
${this.hasSubMenu ? html`<ui-icon class="menu-item-arrow">arrow_right</ui-icon>` : ''}
|
|
215
|
+
</div>`
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { html, PropertyValues, TemplateResult } from 'lit'
|
|
2
|
+
import { property } from 'lit/decorators.js'
|
|
3
|
+
import { classMap } from 'lit/directives/class-map.js'
|
|
4
|
+
import Menu from './Menu.js'
|
|
5
|
+
import UiListItem from '../../list/internals/ListItem.js'
|
|
6
|
+
import { findElementInShadowRoots } from '../../../lib/Dom.js'
|
|
7
|
+
import type { MenuItem } from '../../../index.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Material Design 3 Sub-Menu component.
|
|
11
|
+
* Extends the main Menu component to provide sub-menu functionality.
|
|
12
|
+
* Uses Popover API and Anchor Positioning API for modern positioning.
|
|
13
|
+
*
|
|
14
|
+
* @slot - The sub-menu items
|
|
15
|
+
* @fires select - Dispatched when a sub-menu item is selected
|
|
16
|
+
* @fires close - Dispatched when the sub-menu is closed
|
|
17
|
+
*/
|
|
18
|
+
export default class UiSubMenu extends Menu {
|
|
19
|
+
/**
|
|
20
|
+
* The ID of the anchor element (parent menu item)
|
|
21
|
+
* @attribute
|
|
22
|
+
*/
|
|
23
|
+
@property({ type: String }) accessor anchor: string | undefined
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Reference to the parent menu
|
|
27
|
+
*/
|
|
28
|
+
parentMenu: Menu | null = null
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Reference to the anchor element
|
|
32
|
+
*/
|
|
33
|
+
get anchorElement(): MenuItem | null {
|
|
34
|
+
if (!this.anchor) return null
|
|
35
|
+
return findElementInShadowRoots(this.anchor, this) as MenuItem | null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
override connectedCallback(): void {
|
|
39
|
+
super.connectedCallback()
|
|
40
|
+
this.setAttribute('role', 'menu')
|
|
41
|
+
this.setAttribute('aria-label', 'Submenu')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected override updated(changedProperties: PropertyValues<this>): void {
|
|
45
|
+
super.updated(changedProperties)
|
|
46
|
+
|
|
47
|
+
if (changedProperties.has('anchor')) {
|
|
48
|
+
this.updateAnchorPositioning()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Updates anchor positioning using CSS Anchor Positioning API
|
|
54
|
+
*/
|
|
55
|
+
protected updateAnchorPositioning(): void {
|
|
56
|
+
const anchor = this.anchorElement
|
|
57
|
+
if (!anchor) return
|
|
58
|
+
const anchorName = `--anchor-${this.id}`
|
|
59
|
+
|
|
60
|
+
// Set anchor name on the parent menu item
|
|
61
|
+
anchor.style.setProperty('anchor-name', anchorName)
|
|
62
|
+
|
|
63
|
+
// Set anchor positioning on the submenu
|
|
64
|
+
this.style.setProperty('position-anchor', anchorName)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Shows the submenu
|
|
69
|
+
*/
|
|
70
|
+
override show(): void {
|
|
71
|
+
if (!this.anchorElement) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Update positioning before showing
|
|
76
|
+
this.updateAnchorPositioning()
|
|
77
|
+
|
|
78
|
+
// Close any other open submenus in the parent menu
|
|
79
|
+
if (this.parentMenu) {
|
|
80
|
+
this.parentMenu.closeSubMenu()
|
|
81
|
+
this.parentMenu.setActiveSubMenu(this)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Show the popover
|
|
85
|
+
this.showPopover()
|
|
86
|
+
this.open = true
|
|
87
|
+
this.focus()
|
|
88
|
+
|
|
89
|
+
this.dispatchEvent(
|
|
90
|
+
new CustomEvent('open', {
|
|
91
|
+
bubbles: true,
|
|
92
|
+
composed: true,
|
|
93
|
+
detail: { submenu: this },
|
|
94
|
+
})
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Hides the submenu
|
|
100
|
+
*/
|
|
101
|
+
override hide(): void {
|
|
102
|
+
super.hide()
|
|
103
|
+
|
|
104
|
+
// Clear parent menu's active submenu reference
|
|
105
|
+
const parentMenu = this.parentMenu
|
|
106
|
+
if (parentMenu) {
|
|
107
|
+
parentMenu.setActiveSubMenu(null)
|
|
108
|
+
}
|
|
109
|
+
const anchor = this.anchorElement
|
|
110
|
+
if (anchor) {
|
|
111
|
+
anchor.closeSubMenu()
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Sets the parent menu reference
|
|
117
|
+
*/
|
|
118
|
+
setParentMenu(menu: Menu): void {
|
|
119
|
+
this.parentMenu = menu
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Handles selection events - bubbles them up to parent menu
|
|
124
|
+
*/
|
|
125
|
+
override notifySelect(item: UiListItem): boolean {
|
|
126
|
+
// First hide this submenu
|
|
127
|
+
this.hide()
|
|
128
|
+
|
|
129
|
+
// If we have a parent menu, hide it too and bubble the selection
|
|
130
|
+
if (this.parentMenu) {
|
|
131
|
+
this.parentMenu.hide()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Call parent implementation to dispatch the select event
|
|
135
|
+
return super.notifySelect(item)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Handles keyboard navigation specific to submenus
|
|
140
|
+
*/
|
|
141
|
+
override handleKeydown(e: KeyboardEvent): void {
|
|
142
|
+
if (!this.open || e.defaultPrevented) return
|
|
143
|
+
|
|
144
|
+
switch (e.key) {
|
|
145
|
+
case 'Escape':
|
|
146
|
+
e.preventDefault()
|
|
147
|
+
this.hide()
|
|
148
|
+
// Return focus to parent menu item
|
|
149
|
+
if (this.anchorElement) {
|
|
150
|
+
this.anchorElement.focus()
|
|
151
|
+
}
|
|
152
|
+
break
|
|
153
|
+
case 'ArrowLeft':
|
|
154
|
+
e.preventDefault()
|
|
155
|
+
this.hide()
|
|
156
|
+
// Return focus to parent menu item
|
|
157
|
+
if (this.anchorElement) {
|
|
158
|
+
this.anchorElement.focus()
|
|
159
|
+
}
|
|
160
|
+
break
|
|
161
|
+
default:
|
|
162
|
+
// Let the parent handle other keys
|
|
163
|
+
super.handleKeydown(e)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
override render(): TemplateResult {
|
|
168
|
+
const classes = classMap({
|
|
169
|
+
'submenu-container': true,
|
|
170
|
+
'submenu-open': this.open,
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
return html`
|
|
174
|
+
<div class=${classes}>
|
|
175
|
+
<slot></slot>
|
|
176
|
+
</div>
|
|
177
|
+
`
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CSSResultOrNative } from 'lit'
|
|
2
|
+
import { customElement } from 'lit/decorators.js'
|
|
3
|
+
import Element from './internal/MenuItem.js'
|
|
4
|
+
import styles from './internal/MenuItem.styles.js'
|
|
5
|
+
import listStyles from '../list/internals/ListItem.styles.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Material Design 3 Menu Item component.
|
|
9
|
+
*
|
|
10
|
+
* @element ui-menu-item
|
|
11
|
+
* @attribute {Object} data - The menu item data object
|
|
12
|
+
* @attribute {boolean} disabled - Whether the menu item is disabled
|
|
13
|
+
* @fires select - Dispatched when the menu item is selected
|
|
14
|
+
* @fires submenu-open - Dispatched when a sub-menu is opened
|
|
15
|
+
*/
|
|
16
|
+
@customElement('ui-menu-item')
|
|
17
|
+
export class UiMenuItemElement extends Element {
|
|
18
|
+
static override styles: CSSResultOrNative[] = [styles, listStyles]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare global {
|
|
22
|
+
interface HTMLElementTagNameMap {
|
|
23
|
+
'ui-menu-item': UiMenuItemElement
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CSSResultOrNative } from 'lit'
|
|
2
|
+
import { customElement } from 'lit/decorators.js'
|
|
3
|
+
import Element from './internal/Menu.js'
|
|
4
|
+
import styles from './internal/Menu.styles.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Material Design 3 Menu component with sub-menu support.
|
|
8
|
+
*
|
|
9
|
+
* @element ui-menu
|
|
10
|
+
* @attribute {boolean} open - Whether the menu is currently open
|
|
11
|
+
* @attribute {boolean} anchored - Whether the menu should be positioned relative to an anchor element
|
|
12
|
+
* @attribute {boolean} disabled - Whether the menu is disabled
|
|
13
|
+
* @fires select - Dispatched when a menu item is selected
|
|
14
|
+
* @fires close - Dispatched when the menu is closed
|
|
15
|
+
* @fires open - Dispatched when the menu is opened
|
|
16
|
+
*/
|
|
17
|
+
@customElement('ui-menu')
|
|
18
|
+
export class UiMenuElement extends Element {
|
|
19
|
+
static override styles: CSSResultOrNative[] = [styles]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare global {
|
|
23
|
+
interface HTMLElementTagNameMap {
|
|
24
|
+
'ui-menu': UiMenuElement
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CSSResultOrNative } from 'lit'
|
|
2
|
+
import { customElement } from 'lit/decorators.js'
|
|
3
|
+
import Element from './internal/SubMenu.js'
|
|
4
|
+
import menuStyles from './internal/SubMenu.styles.js'
|
|
5
|
+
import styles from './internal/Menu.styles.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Material Design 3 Sub-Menu component.
|
|
9
|
+
*
|
|
10
|
+
* @element ui-sub-menu
|
|
11
|
+
* @attribute {Object} parentItem - The parent menu item element
|
|
12
|
+
* @attribute {Array} menuItems - The sub-menu items data
|
|
13
|
+
* @fires select - Dispatched when a sub-menu item is selected
|
|
14
|
+
* @fires close - Dispatched when the sub-menu is closed
|
|
15
|
+
*/
|
|
16
|
+
@customElement('ui-sub-menu')
|
|
17
|
+
export class UiSubMenuElement extends Element {
|
|
18
|
+
static override styles: CSSResultOrNative[] = [styles, menuStyles]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare global {
|
|
22
|
+
interface HTMLElementTagNameMap {
|
|
23
|
+
'ui-sub-menu': UiSubMenuElement
|
|
24
|
+
}
|
|
25
|
+
}
|