@api-client/ui 0.5.21 → 0.5.23
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/.github/instructions/lit-best-practices.instructions.md +1 -0
- package/build/src/md/button/ui-button-group.d.ts +1 -0
- package/build/src/md/button/ui-button-group.d.ts.map +1 -1
- package/build/src/md/button/ui-button-group.js.map +1 -1
- package/build/src/md/button/ui-button.d.ts +2 -0
- package/build/src/md/button/ui-button.d.ts.map +1 -1
- package/build/src/md/button/ui-button.js.map +1 -1
- package/build/src/md/chip/ui-chip.d.ts +1 -0
- package/build/src/md/chip/ui-chip.d.ts.map +1 -1
- package/build/src/md/chip/ui-chip.js +1 -0
- package/build/src/md/chip/ui-chip.js.map +1 -1
- package/build/src/md/dialog/internals/Dialog.d.ts +0 -1
- package/build/src/md/dialog/internals/Dialog.d.ts.map +1 -1
- package/build/src/md/dialog/internals/Dialog.js.map +1 -1
- package/build/src/md/dialog/ui-dialog.d.ts +1 -0
- package/build/src/md/dialog/ui-dialog.d.ts.map +1 -1
- package/build/src/md/dialog/ui-dialog.js.map +1 -1
- package/build/src/md/divider/ui-divider.d.ts +1 -0
- package/build/src/md/divider/ui-divider.d.ts.map +1 -1
- package/build/src/md/divider/ui-divider.js +1 -0
- package/build/src/md/divider/ui-divider.js.map +1 -1
- package/build/src/md/dropdown-list/ui-dropdown-list.d.ts +1 -0
- package/build/src/md/dropdown-list/ui-dropdown-list.d.ts.map +1 -1
- package/build/src/md/dropdown-list/ui-dropdown-list.js.map +1 -1
- package/build/src/md/icon-button/ui-icon-button.d.ts +2 -0
- package/build/src/md/icon-button/ui-icon-button.d.ts.map +1 -1
- package/build/src/md/icon-button/ui-icon-button.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 +7 -0
- package/build/src/md/list/internals/ListItem.styles.js.map +1 -1
- package/build/src/md/list/ui-list-item.d.ts +1 -0
- package/build/src/md/list/ui-list-item.d.ts.map +1 -1
- package/build/src/md/list/ui-list-item.js +1 -0
- package/build/src/md/list/ui-list-item.js.map +1 -1
- package/build/src/md/list/ui-list.d.ts +1 -0
- package/build/src/md/list/ui-list.d.ts.map +1 -1
- package/build/src/md/list/ui-list.js.map +1 -1
- package/build/src/md/menu/internal/Menu.d.ts +13 -0
- package/build/src/md/menu/internal/Menu.d.ts.map +1 -1
- package/build/src/md/menu/internal/Menu.js +47 -31
- package/build/src/md/menu/internal/Menu.js.map +1 -1
- package/build/src/md/menu/internal/Menu.styles.d.ts.map +1 -1
- package/build/src/md/menu/internal/Menu.styles.js +14 -2
- package/build/src/md/menu/internal/Menu.styles.js.map +1 -1
- package/build/src/md/menu/internal/MenuItem.d.ts +22 -0
- package/build/src/md/menu/internal/MenuItem.d.ts.map +1 -1
- package/build/src/md/menu/internal/MenuItem.js +74 -1
- package/build/src/md/menu/internal/MenuItem.js.map +1 -1
- package/build/src/md/menu/internal/MenuItem.styles.d.ts.map +1 -1
- package/build/src/md/menu/internal/MenuItem.styles.js +23 -0
- package/build/src/md/menu/internal/MenuItem.styles.js.map +1 -1
- package/build/src/md/menu/ui-menu-item.d.ts +1 -4
- package/build/src/md/menu/ui-menu-item.d.ts.map +1 -1
- package/build/src/md/menu/ui-menu-item.js +1 -4
- package/build/src/md/menu/ui-menu-item.js.map +1 -1
- package/build/src/md/menu/ui-menu.d.ts +1 -0
- package/build/src/md/menu/ui-menu.d.ts.map +1 -1
- package/build/src/md/menu/ui-menu.js.map +1 -1
- package/build/src/md/tabs/ui-tabs.d.ts +1 -0
- package/build/src/md/tabs/ui-tabs.d.ts.map +1 -1
- package/build/src/md/tabs/ui-tabs.js.map +1 -1
- package/demo/md/menu/index.ts +146 -1
- package/package.json +1 -1
- package/src/md/button/ui-button-group.ts +2 -0
- package/src/md/button/ui-button.ts +3 -0
- package/src/md/chip/ui-chip.ts +2 -0
- package/src/md/dialog/internals/Dialog.ts +0 -2
- package/src/md/dialog/ui-dialog.ts +2 -0
- package/src/md/divider/ui-divider.ts +2 -0
- package/src/md/dropdown-list/ui-dropdown-list.ts +2 -0
- package/src/md/icon-button/ui-icon-button.ts +3 -0
- package/src/md/list/internals/ListItem.styles.ts +7 -0
- package/src/md/list/ui-list-item.ts +2 -0
- package/src/md/list/ui-list.ts +2 -0
- package/src/md/menu/internal/Menu.styles.ts +14 -2
- package/src/md/menu/internal/Menu.ts +53 -32
- package/src/md/menu/internal/MenuItem.styles.ts +23 -0
- package/src/md/menu/internal/MenuItem.ts +49 -0
- package/src/md/menu/ui-menu-item.ts +2 -4
- package/src/md/menu/ui-menu.ts +2 -0
- package/src/md/tabs/ui-tabs.ts +2 -0
- package/test/md/menu/Menu.test.ts +346 -0
- package/test/md/menu/MenuItem.test.ts +292 -0
package/demo/md/menu/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { html, TemplateResult } from 'lit'
|
|
2
2
|
import { DemoPage } from '../../../src/demo/DemoPage.js'
|
|
3
3
|
import reactive from '../../../src/decorators/reactive.js'
|
|
4
|
+
import type { UiMenuItemElement } from '../../../src/md/menu/ui-menu-item.js'
|
|
4
5
|
|
|
5
6
|
import '../../../src/md/menu/ui-menu.js'
|
|
6
7
|
import '../../../src/md/menu/ui-sub-menu.js'
|
|
@@ -17,6 +18,11 @@ class ComponentDemoPage extends DemoPage {
|
|
|
17
18
|
@reactive() accessor nestedCount = 0
|
|
18
19
|
@reactive() accessor overflowMenuOutput = ''
|
|
19
20
|
@reactive() accessor overflowCount = 0
|
|
21
|
+
@reactive() accessor selectedOption = ''
|
|
22
|
+
@reactive() accessor iconMenuSelectOutput = ''
|
|
23
|
+
@reactive() accessor iconMenuSelectedOption = 'Premium'
|
|
24
|
+
@reactive() accessor autoCheckMenuOutput = ''
|
|
25
|
+
@reactive() accessor autoCheckSelectedOption = 'High'
|
|
20
26
|
|
|
21
27
|
handleBasicMenuSelect(e: CustomEvent): void {
|
|
22
28
|
const item = e.detail.item as HTMLElement
|
|
@@ -39,8 +45,37 @@ class ComponentDemoPage extends DemoPage {
|
|
|
39
45
|
this.overflowCount++
|
|
40
46
|
}
|
|
41
47
|
|
|
48
|
+
handleSelectionMenuSelect(e: CustomEvent): void {
|
|
49
|
+
const item = e.detail.item as HTMLElement
|
|
50
|
+
this.selectedOption = item.textContent?.trim() || ''
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
handleIconSelectMenuSelect(e: CustomEvent): void {
|
|
54
|
+
const item = e.detail.item as UiMenuItemElement
|
|
55
|
+
this.iconMenuSelectedOption = item.value || ''
|
|
56
|
+
this.iconMenuSelectOutput = `Selected: ${this.iconMenuSelectedOption}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
handleAutoCheckMenuSelect(e: CustomEvent): void {
|
|
60
|
+
const item = e.detail.item as HTMLElement
|
|
61
|
+
this.autoCheckSelectedOption = item.textContent?.trim() || ''
|
|
62
|
+
this.autoCheckMenuOutput = `Selected: ${this.autoCheckSelectedOption}`
|
|
63
|
+
}
|
|
64
|
+
|
|
42
65
|
contentTemplate(): TemplateResult {
|
|
43
|
-
const {
|
|
66
|
+
const {
|
|
67
|
+
basicMenuOutput,
|
|
68
|
+
nestedMenuOutput,
|
|
69
|
+
nestedCount,
|
|
70
|
+
basicCount,
|
|
71
|
+
overflowMenuOutput,
|
|
72
|
+
overflowCount,
|
|
73
|
+
selectedOption,
|
|
74
|
+
iconMenuSelectOutput,
|
|
75
|
+
iconMenuSelectedOption,
|
|
76
|
+
autoCheckMenuOutput,
|
|
77
|
+
autoCheckSelectedOption,
|
|
78
|
+
} = this
|
|
44
79
|
return html`
|
|
45
80
|
<a href="../">Back</a>
|
|
46
81
|
|
|
@@ -361,6 +396,116 @@ class ComponentDemoPage extends DemoPage {
|
|
|
361
396
|
<p>Count: ${nestedCount}</p>`
|
|
362
397
|
: ''}
|
|
363
398
|
</section>
|
|
399
|
+
|
|
400
|
+
<section class="demo-section">
|
|
401
|
+
<h2 class="title-large">Selection Menu</h2>
|
|
402
|
+
<p>A menu that allows selection of options:</p>
|
|
403
|
+
<ui-button id="selection-menu-trigger" color="filled" popovertarget="selection-menu"
|
|
404
|
+
>Open Selection Menu</ui-button
|
|
405
|
+
>
|
|
406
|
+
<ui-menu id="selection-menu" popover="auto" @select="${this.handleSelectionMenuSelect}">
|
|
407
|
+
<ui-menu-item data-value="option1" ?selected="${selectedOption === 'Option 1'}">
|
|
408
|
+
${selectedOption === 'Option 1' ? html`<span slot="start"><ui-icon>check</ui-icon></span>` : ''}
|
|
409
|
+
<span>Option 1</span>
|
|
410
|
+
</ui-menu-item>
|
|
411
|
+
<ui-menu-item data-value="option2" ?selected="${selectedOption === 'Option 2'}">
|
|
412
|
+
${selectedOption === 'Option 2' ? html`<span slot="start"><ui-icon>check</ui-icon></span>` : ''}
|
|
413
|
+
<span>Option 2</span>
|
|
414
|
+
</ui-menu-item>
|
|
415
|
+
<ui-menu-item data-value="option3" ?selected="${selectedOption === 'Option 3'}">
|
|
416
|
+
${selectedOption === 'Option 3' ? html`<span slot="start"><ui-icon>check</ui-icon></span>` : ''}
|
|
417
|
+
<span>Option 3</span>
|
|
418
|
+
</ui-menu-item>
|
|
419
|
+
</ui-menu>
|
|
420
|
+
${selectedOption ? html`<p>Selected: ${selectedOption}</p>` : ''}
|
|
421
|
+
</section>
|
|
422
|
+
|
|
423
|
+
<section class="demo-section">
|
|
424
|
+
<h2 class="title-large">Menu with Selection and icons</h2>
|
|
425
|
+
<p>
|
|
426
|
+
A menu that maintains selection state. Select an option and reopen the menu to see the selection preserved:
|
|
427
|
+
</p>
|
|
428
|
+
<ui-button id="single-select-menu-trigger" color="filled" popovertarget="single-select-menu">
|
|
429
|
+
Select Option: ${iconMenuSelectedOption || 'None'}
|
|
430
|
+
</ui-button>
|
|
431
|
+
<ui-menu id="single-select-menu" popover="auto" @select="${this.handleIconSelectMenuSelect}">
|
|
432
|
+
<ui-menu-item value="premium" showSelectionIcon ?selected="${iconMenuSelectedOption === 'premium'}">
|
|
433
|
+
<span slot="start"><ui-icon>star</ui-icon></span>
|
|
434
|
+
<span>Premium</span>
|
|
435
|
+
</ui-menu-item>
|
|
436
|
+
<ui-menu-item value="standard" showSelectionIcon ?selected="${iconMenuSelectedOption === 'standard'}">
|
|
437
|
+
<span slot="start"><ui-icon>account_circle</ui-icon></span>
|
|
438
|
+
<span>Standard</span>
|
|
439
|
+
</ui-menu-item>
|
|
440
|
+
<ui-menu-item value="basic" showSelectionIcon ?selected="${iconMenuSelectedOption === 'basic'}">
|
|
441
|
+
<span slot="start"><ui-icon>public</ui-icon></span>
|
|
442
|
+
<span>Basic</span>
|
|
443
|
+
</ui-menu-item>
|
|
444
|
+
</ui-menu>
|
|
445
|
+
${iconMenuSelectOutput ? html`<p>${iconMenuSelectOutput}</p>` : ''}
|
|
446
|
+
</section>
|
|
447
|
+
|
|
448
|
+
<section class="demo-section">
|
|
449
|
+
<h2 class="title-large">Menu with Auto-Check Icons</h2>
|
|
450
|
+
<p>
|
|
451
|
+
A menu that automatically shows check icons for selected items when the
|
|
452
|
+
<code>show-selection-icon</code> attribute is used:
|
|
453
|
+
</p>
|
|
454
|
+
<ui-button id="auto-check-menu-trigger" color="filled" popovertarget="auto-check-menu">
|
|
455
|
+
Priority: ${autoCheckSelectedOption || 'None'}
|
|
456
|
+
</ui-button>
|
|
457
|
+
<ui-menu id="auto-check-menu" popover="auto" @select="${this.handleAutoCheckMenuSelect}">
|
|
458
|
+
<ui-menu-item showSelectionIcon>
|
|
459
|
+
<span>Low</span>
|
|
460
|
+
</ui-menu-item>
|
|
461
|
+
<ui-menu-item showSelectionIcon selected>
|
|
462
|
+
<span>High</span>
|
|
463
|
+
</ui-menu-item>
|
|
464
|
+
<ui-menu-item showSelectionIcon>
|
|
465
|
+
<span>Critical</span>
|
|
466
|
+
</ui-menu-item>
|
|
467
|
+
</ui-menu>
|
|
468
|
+
${autoCheckMenuOutput ? html`<p>${autoCheckMenuOutput}</p>` : ''}
|
|
469
|
+
</section>
|
|
470
|
+
|
|
471
|
+
<section class="demo-section">
|
|
472
|
+
<h2 class="title-large">Edge Positioning Test</h2>
|
|
473
|
+
<p>Test menu positioning at screen edges:</p>
|
|
474
|
+
<div style="display: flex; justify-content: space-between; margin: 20px 0;">
|
|
475
|
+
<ui-button id="left-edge-menu-trigger" color="filled" popovertarget="left-edge-menu">Left Edge</ui-button>
|
|
476
|
+
<ui-button id="right-edge-menu-trigger" color="filled" popovertarget="right-edge-menu">Right Edge</ui-button>
|
|
477
|
+
</div>
|
|
478
|
+
|
|
479
|
+
<ui-menu id="left-edge-menu" popover="auto" @select="${this.handleBasicMenuSelect}">
|
|
480
|
+
<ui-menu-item>
|
|
481
|
+
<span slot="start"><ui-icon>check</ui-icon></span>
|
|
482
|
+
<span>Left Edge Menu Item 1</span>
|
|
483
|
+
</ui-menu-item>
|
|
484
|
+
<ui-menu-item>
|
|
485
|
+
<span slot="start"><ui-icon>check</ui-icon></span>
|
|
486
|
+
<span>Left Edge Menu Item 2</span>
|
|
487
|
+
</ui-menu-item>
|
|
488
|
+
<ui-menu-item>
|
|
489
|
+
<span slot="start"><ui-icon>check</ui-icon></span>
|
|
490
|
+
<span>Left Edge Menu Item 3</span>
|
|
491
|
+
</ui-menu-item>
|
|
492
|
+
</ui-menu>
|
|
493
|
+
|
|
494
|
+
<ui-menu id="right-edge-menu" popover="auto" @select="${this.handleBasicMenuSelect}">
|
|
495
|
+
<ui-menu-item>
|
|
496
|
+
<span slot="start"><ui-icon>check</ui-icon></span>
|
|
497
|
+
<span>Right Edge Menu Item 1</span>
|
|
498
|
+
</ui-menu-item>
|
|
499
|
+
<ui-menu-item>
|
|
500
|
+
<span slot="start"><ui-icon>check</ui-icon></span>
|
|
501
|
+
<span>Right Edge Menu Item 2</span>
|
|
502
|
+
</ui-menu-item>
|
|
503
|
+
<ui-menu-item>
|
|
504
|
+
<span slot="start"><ui-icon>check</ui-icon></span>
|
|
505
|
+
<span>Right Edge Menu Item 3</span>
|
|
506
|
+
</ui-menu-item>
|
|
507
|
+
</ui-menu>
|
|
508
|
+
</section>
|
|
364
509
|
`
|
|
365
510
|
}
|
|
366
511
|
}
|
package/package.json
CHANGED
package/src/md/chip/ui-chip.ts
CHANGED
|
@@ -27,8 +27,6 @@ interface DialogEventMap {
|
|
|
27
27
|
close: CustomEvent<UiDialogClosingReason>
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export type RenderFunction = () => TemplateResult
|
|
31
|
-
|
|
32
30
|
/**
|
|
33
31
|
* Styled dialog using a native `<dialog>` element under the hood.
|
|
34
32
|
* Note, since native dialog renders in the top layer it is not necessary
|
|
@@ -123,6 +123,13 @@ export default css`
|
|
|
123
123
|
line-height: var(--md-sys-typescale-body-small-height);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
.start {
|
|
127
|
+
align-self: stretch;
|
|
128
|
+
display: flex;
|
|
129
|
+
justify-content: center;
|
|
130
|
+
align-items: center;
|
|
131
|
+
}
|
|
132
|
+
|
|
126
133
|
:host slot[name='end']::slotted(*) {
|
|
127
134
|
color: var(--md-sys-color-on-surface-variant);
|
|
128
135
|
fill: var(--md-sys-color-on-surface-variant);
|
package/src/md/list/ui-list.ts
CHANGED
|
@@ -4,7 +4,7 @@ export default css`
|
|
|
4
4
|
:host {
|
|
5
5
|
display: none;
|
|
6
6
|
position-area: bottom span-right;
|
|
7
|
-
position-try:
|
|
7
|
+
position-try: --menu-fallback-bottom-left, --menu-fallback-top-right, --menu-fallback-top-left, flip-block;
|
|
8
8
|
position: absolute;
|
|
9
9
|
margin: 0;
|
|
10
10
|
padding: 0;
|
|
@@ -15,9 +15,21 @@ export default css`
|
|
|
15
15
|
overflow: auto;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
@position-try --menu-fallback-bottom-left {
|
|
19
|
+
position-area: bottom span-left;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@position-try --menu-fallback-top-right {
|
|
23
|
+
position-area: top span-right;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@position-try --menu-fallback-top-left {
|
|
27
|
+
position-area: top span-left;
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
:host(:popover-open) {
|
|
19
31
|
display: block;
|
|
20
|
-
background-color: var(--md-sys-color-surface);
|
|
32
|
+
background-color: var(--md-sys-color-surface-container-low);
|
|
21
33
|
border-radius: var(--md-sys-shape-corner-extra-small);
|
|
22
34
|
box-shadow: var(--md-sys-elevation-3);
|
|
23
35
|
}
|
|
@@ -15,6 +15,7 @@ import { bound } from '../../../decorators/bound.js'
|
|
|
15
15
|
*
|
|
16
16
|
* @fires select - Dispatched when a menu item is selected
|
|
17
17
|
* @fires close - Dispatched when the menu is closed
|
|
18
|
+
* @fires open - Dispatched when the menu is opened
|
|
18
19
|
*/
|
|
19
20
|
export default class Menu extends UiList {
|
|
20
21
|
/**
|
|
@@ -104,41 +105,29 @@ export default class Menu extends UiList {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
positionMenu(): void {
|
|
107
|
-
//
|
|
108
|
+
// Let CSS anchor positioning handle the positioning automatically
|
|
109
|
+
// Only intervene if we need to set max-height for overflow cases
|
|
108
110
|
const box = this.getBoundingClientRect()
|
|
109
|
-
// Now, we determine, whether to position the menu above or below the anchor
|
|
110
|
-
// in a way, that if we have enough space below the anchor, we position it below,
|
|
111
|
-
// otherwise we position it above the anchor.
|
|
112
|
-
|
|
113
|
-
// our starting point is the anchor being positioned below the anchor
|
|
114
111
|
const menuBottom = box.top + box.height
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
const menuRight = box.left + box.width
|
|
113
|
+
|
|
114
|
+
// Reset any previous manual positioning to let CSS anchor positioning work
|
|
115
|
+
this.style.removeProperty('position-area')
|
|
116
|
+
this.style.removeProperty('max-height')
|
|
117
|
+
|
|
118
|
+
// Only set max-height if the menu would overflow
|
|
119
|
+
if (menuBottom > innerHeight) {
|
|
120
|
+
const availableHeight = innerHeight - box.top
|
|
121
|
+
this.style.maxHeight = `${Math.max(200, availableHeight - 20)}px`
|
|
118
122
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const spaceBelow = innerHeight - anchorBottom
|
|
128
|
-
const spaceAbove = anchorTop
|
|
129
|
-
|
|
130
|
-
const diffBelow = spaceBelow - menuHeight
|
|
131
|
-
const diffAbove = spaceAbove - menuHeight
|
|
132
|
-
// The initial check ensures the menu does not fit below. Now, check if it fits above.
|
|
133
|
-
if (diffAbove >= 0) {
|
|
134
|
-
this.style.setProperty('position-area', 'top span-right')
|
|
135
|
-
} else if (diffAbove > diffBelow) {
|
|
136
|
-
// It doesn't fit in either direction. Choose the one with less overflow (larger, i.e., less negative, diff).
|
|
137
|
-
this.style.setProperty('position-area', 'top span-right')
|
|
138
|
-
this.style.maxHeight = `${spaceAbove}px`
|
|
139
|
-
} else {
|
|
140
|
-
this.style.setProperty('position-area', 'bottom span-right')
|
|
141
|
-
this.style.maxHeight = `${spaceBelow}px`
|
|
123
|
+
|
|
124
|
+
if (menuRight > innerWidth) {
|
|
125
|
+
const availableWidth = innerWidth - box.left
|
|
126
|
+
// Let CSS anchor positioning handle horizontal flipping
|
|
127
|
+
// We could set max-width if needed
|
|
128
|
+
if (availableWidth < 200) {
|
|
129
|
+
this.style.maxWidth = `${Math.max(180, availableWidth - 20)}px`
|
|
130
|
+
}
|
|
142
131
|
}
|
|
143
132
|
}
|
|
144
133
|
|
|
@@ -213,10 +202,42 @@ export default class Menu extends UiList {
|
|
|
213
202
|
}
|
|
214
203
|
|
|
215
204
|
override notifySelect(item: UiListItem, index?: number): boolean {
|
|
205
|
+
// Handle single selection
|
|
206
|
+
if (item instanceof UiMenuItem) {
|
|
207
|
+
this.clearSelection()
|
|
208
|
+
item.selected = true
|
|
209
|
+
}
|
|
210
|
+
|
|
216
211
|
this.hide()
|
|
217
212
|
return super.notifySelect(item, index)
|
|
218
213
|
}
|
|
219
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Clears selection from all menu items
|
|
217
|
+
*/
|
|
218
|
+
protected clearSelection(): void {
|
|
219
|
+
this.assignedMenuItems.forEach((menuItem) => {
|
|
220
|
+
menuItem.selected = false
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Gets the currently selected menu item
|
|
226
|
+
*/
|
|
227
|
+
get selectedItem(): UiMenuItem | null {
|
|
228
|
+
return this.assignedMenuItems.find((item) => item.selected) || null
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Sets the selected menu item
|
|
233
|
+
*/
|
|
234
|
+
setSelectedItem(item: UiMenuItem | null): void {
|
|
235
|
+
this.clearSelection()
|
|
236
|
+
if (item) {
|
|
237
|
+
item.selected = true
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
220
241
|
/**
|
|
221
242
|
* Handles sub-menu opening
|
|
222
243
|
*/
|
|
@@ -54,6 +54,7 @@ export default css`
|
|
|
54
54
|
md-focus-ring {
|
|
55
55
|
--md-focus-ring-color: var(--md-sys-color-primary);
|
|
56
56
|
--md-focus-ring-width: 2px;
|
|
57
|
+
z-index: 2;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
/* Ripple Effect */
|
|
@@ -61,4 +62,26 @@ export default css`
|
|
|
61
62
|
--md-ripple-color: var(--md-sys-color-primary);
|
|
62
63
|
--md-ripple-opacity: 0.12;
|
|
63
64
|
}
|
|
65
|
+
|
|
66
|
+
/* Selected state */
|
|
67
|
+
:host(.select) .menu-item,
|
|
68
|
+
:host([selected]) .menu-item {
|
|
69
|
+
background-color: var(--md-sys-color-secondary-container);
|
|
70
|
+
color: var(--md-sys-color-on-secondary-container);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
:host(.select) .menu-item:hover,
|
|
74
|
+
:host([selected]) .menu-item:hover {
|
|
75
|
+
background-color: var(--md-sys-color-secondary-container);
|
|
76
|
+
opacity: 0.92;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Selection check icon */
|
|
80
|
+
.selection-check {
|
|
81
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
82
|
+
fill: var(--md-sys-color-on-surface-variant);
|
|
83
|
+
width: 24px;
|
|
84
|
+
height: 24px;
|
|
85
|
+
margin-right: 8px;
|
|
86
|
+
}
|
|
64
87
|
`
|
|
@@ -8,6 +8,7 @@ import { nanoid } from 'nanoid'
|
|
|
8
8
|
|
|
9
9
|
import '@material/web/focus/md-focus-ring.js'
|
|
10
10
|
import '../../ripple/ui-ripple.js'
|
|
11
|
+
import '../../icons/ui-icon.js'
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Material Design 3 Menu Item component.
|
|
@@ -23,6 +24,25 @@ export default class UiMenuItem extends UiListItem {
|
|
|
23
24
|
*/
|
|
24
25
|
@property({ type: String }) accessor submenu: string | undefined
|
|
25
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Whether this menu item is selected
|
|
29
|
+
* @attribute
|
|
30
|
+
*/
|
|
31
|
+
@property({ type: Boolean, reflect: true }) accessor selected = false
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The value associated with this menu item. Use it to identify value associated with the menu item,
|
|
35
|
+
* when selected.
|
|
36
|
+
* @attribute
|
|
37
|
+
*/
|
|
38
|
+
@property({ type: String, reflect: true }) accessor value: string | undefined
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Whether to automatically show a check icon when selected
|
|
42
|
+
* @attribute
|
|
43
|
+
*/
|
|
44
|
+
@property({ type: Boolean }) accessor showSelectionIcon = false
|
|
45
|
+
|
|
26
46
|
/**
|
|
27
47
|
* Whether the menu item has a sub-menu
|
|
28
48
|
*/
|
|
@@ -58,6 +78,9 @@ export default class UiMenuItem extends UiListItem {
|
|
|
58
78
|
if (!this.id) {
|
|
59
79
|
this.id = nanoid(6)
|
|
60
80
|
}
|
|
81
|
+
|
|
82
|
+
// Initialize selection state
|
|
83
|
+
this.updateSelectionState()
|
|
61
84
|
}
|
|
62
85
|
|
|
63
86
|
protected override updated(changedProperties: PropertyValues<this>): void {
|
|
@@ -67,6 +90,10 @@ export default class UiMenuItem extends UiListItem {
|
|
|
67
90
|
this.updateAccessibility()
|
|
68
91
|
this.setupSubmenuConnection()
|
|
69
92
|
}
|
|
93
|
+
|
|
94
|
+
if (changedProperties.has('selected')) {
|
|
95
|
+
this.updateSelectionState()
|
|
96
|
+
}
|
|
70
97
|
}
|
|
71
98
|
|
|
72
99
|
/**
|
|
@@ -97,6 +124,19 @@ export default class UiMenuItem extends UiListItem {
|
|
|
97
124
|
}
|
|
98
125
|
}
|
|
99
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Updates the selection state styling
|
|
129
|
+
*/
|
|
130
|
+
protected updateSelectionState(): void {
|
|
131
|
+
if (this.selected) {
|
|
132
|
+
this.classList.add('select')
|
|
133
|
+
this.setAttribute('aria-selected', 'true')
|
|
134
|
+
} else {
|
|
135
|
+
this.classList.remove('select')
|
|
136
|
+
this.setAttribute('aria-selected', 'false')
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
100
140
|
/**
|
|
101
141
|
* Handles mouse enter events
|
|
102
142
|
*/
|
|
@@ -214,4 +254,13 @@ export default class UiMenuItem extends UiListItem {
|
|
|
214
254
|
${this.hasSubMenu ? html`<ui-icon class="menu-item-arrow">arrow_right</ui-icon>` : ''}
|
|
215
255
|
</div>`
|
|
216
256
|
}
|
|
257
|
+
|
|
258
|
+
protected override renderStart(): TemplateResult {
|
|
259
|
+
const showCheckIcon = this.showSelectionIcon && this.selected
|
|
260
|
+
|
|
261
|
+
return html`<div class="start">
|
|
262
|
+
${showCheckIcon ? html`<ui-icon class="selection-check">check</ui-icon>` : ''}
|
|
263
|
+
<slot name="start" @slotchange=${this.handleSlotChange}></slot>
|
|
264
|
+
</div>`
|
|
265
|
+
}
|
|
217
266
|
}
|
|
@@ -8,10 +8,6 @@ import listStyles from '../list/internals/ListItem.styles.js'
|
|
|
8
8
|
* Material Design 3 Menu Item component.
|
|
9
9
|
*
|
|
10
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
11
|
*/
|
|
16
12
|
@customElement('ui-menu-item')
|
|
17
13
|
export class UiMenuItemElement extends Element {
|
|
@@ -23,3 +19,5 @@ declare global {
|
|
|
23
19
|
'ui-menu-item': UiMenuItemElement
|
|
24
20
|
}
|
|
25
21
|
}
|
|
22
|
+
|
|
23
|
+
export { ListItemImage, ListItemLines } from '../list/internals/ListItem.js'
|
package/src/md/menu/ui-menu.ts
CHANGED