@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.
Files changed (111) hide show
  1. package/.vscode/settings.json +1 -0
  2. package/build/src/index.d.ts +3 -0
  3. package/build/src/index.d.ts.map +1 -1
  4. package/build/src/index.js +4 -0
  5. package/build/src/index.js.map +1 -1
  6. package/build/src/lib/Dom.d.ts +5 -0
  7. package/build/src/lib/Dom.d.ts.map +1 -0
  8. package/build/src/lib/Dom.js +40 -0
  9. package/build/src/lib/Dom.js.map +1 -0
  10. package/build/src/md/button/internals/base.d.ts +27 -0
  11. package/build/src/md/button/internals/base.d.ts.map +1 -1
  12. package/build/src/md/button/internals/base.js +90 -1
  13. package/build/src/md/button/internals/base.js.map +1 -1
  14. package/build/src/md/icons/internals/Icon.js +2 -2
  15. package/build/src/md/icons/internals/Icon.js.map +1 -1
  16. package/build/src/md/list/internals/List.d.ts +1 -1
  17. package/build/src/md/list/internals/List.d.ts.map +1 -1
  18. package/build/src/md/list/internals/List.js +4 -4
  19. package/build/src/md/list/internals/List.js.map +1 -1
  20. package/build/src/md/list/internals/ListItem.d.ts +1 -0
  21. package/build/src/md/list/internals/ListItem.d.ts.map +1 -1
  22. package/build/src/md/list/internals/ListItem.js +10 -10
  23. package/build/src/md/list/internals/ListItem.js.map +1 -1
  24. package/build/src/md/list/internals/ListItem.styles.d.ts.map +1 -1
  25. package/build/src/md/list/internals/ListItem.styles.js +2 -20
  26. package/build/src/md/list/internals/ListItem.styles.js.map +1 -1
  27. package/build/src/md/menu/index.d.ts +4 -0
  28. package/build/src/md/menu/index.d.ts.map +1 -0
  29. package/build/src/md/menu/index.js +4 -0
  30. package/build/src/md/menu/index.js.map +1 -0
  31. package/build/src/md/menu/internal/Menu.d.ts +76 -0
  32. package/build/src/md/menu/internal/Menu.d.ts.map +1 -0
  33. package/build/src/md/menu/internal/Menu.js +235 -0
  34. package/build/src/md/menu/internal/Menu.js.map +1 -0
  35. package/build/src/md/menu/internal/Menu.styles.d.ts +3 -0
  36. package/build/src/md/menu/internal/Menu.styles.d.ts.map +1 -0
  37. package/build/src/md/menu/internal/Menu.styles.js +185 -0
  38. package/build/src/md/menu/internal/Menu.styles.js.map +1 -0
  39. package/build/src/md/menu/internal/MenuItem.d.ts +77 -0
  40. package/build/src/md/menu/internal/MenuItem.d.ts.map +1 -0
  41. package/build/src/md/menu/internal/MenuItem.js +216 -0
  42. package/build/src/md/menu/internal/MenuItem.js.map +1 -0
  43. package/build/src/md/menu/internal/MenuItem.styles.d.ts +3 -0
  44. package/build/src/md/menu/internal/MenuItem.styles.d.ts.map +1 -0
  45. package/build/src/md/menu/internal/MenuItem.styles.js +64 -0
  46. package/build/src/md/menu/internal/MenuItem.styles.js.map +1 -0
  47. package/build/src/md/menu/internal/SubMenu.d.ts +56 -0
  48. package/build/src/md/menu/internal/SubMenu.d.ts.map +1 -0
  49. package/build/src/md/menu/internal/SubMenu.js +171 -0
  50. package/build/src/md/menu/internal/SubMenu.js.map +1 -0
  51. package/build/src/md/menu/internal/SubMenu.styles.d.ts +3 -0
  52. package/build/src/md/menu/internal/SubMenu.styles.d.ts.map +1 -0
  53. package/build/src/md/menu/internal/SubMenu.styles.js +8 -0
  54. package/build/src/md/menu/internal/SubMenu.styles.js.map +1 -0
  55. package/build/src/md/menu/ui-menu-item.d.ts +20 -0
  56. package/build/src/md/menu/ui-menu-item.d.ts.map +1 -0
  57. package/build/src/md/menu/ui-menu-item.js +37 -0
  58. package/build/src/md/menu/ui-menu-item.js.map +1 -0
  59. package/build/src/md/menu/ui-menu.d.ts +22 -0
  60. package/build/src/md/menu/ui-menu.d.ts.map +1 -0
  61. package/build/src/md/menu/ui-menu.js +38 -0
  62. package/build/src/md/menu/ui-menu.js.map +1 -0
  63. package/build/src/md/menu/ui-sub-menu.d.ts +20 -0
  64. package/build/src/md/menu/ui-sub-menu.d.ts.map +1 -0
  65. package/build/src/md/menu/ui-sub-menu.js +37 -0
  66. package/build/src/md/menu/ui-sub-menu.js.map +1 -0
  67. package/build/src/mixins/FileDropMixin.d.ts.map +1 -1
  68. package/build/src/mixins/FileDropMixin.js +7 -8
  69. package/build/src/mixins/FileDropMixin.js.map +1 -1
  70. package/build/src/mixins/RenderableMixin.d.ts.map +1 -1
  71. package/build/src/mixins/RenderableMixin.js +2 -3
  72. package/build/src/mixins/RenderableMixin.js.map +1 -1
  73. package/demo/md/index.html +2 -0
  74. package/demo/md/menu/index.html +19 -0
  75. package/demo/md/menu/index.ts +154 -0
  76. package/package.json +2 -3
  77. package/src/index.ts +5 -0
  78. package/src/lib/Dom.ts +41 -0
  79. package/src/md/button/internals/base.ts +77 -0
  80. package/src/md/icons/internals/Icon.ts +2 -2
  81. package/src/md/list/internals/List.ts +4 -4
  82. package/src/md/list/internals/ListItem.styles.ts +2 -20
  83. package/src/md/list/internals/ListItem.ts +11 -11
  84. package/src/md/menu/README.md +253 -0
  85. package/src/md/menu/index.ts +3 -0
  86. package/src/md/menu/internal/Menu.styles.ts +185 -0
  87. package/src/md/menu/internal/Menu.ts +205 -0
  88. package/src/md/menu/internal/MenuItem.styles.ts +64 -0
  89. package/src/md/menu/internal/MenuItem.ts +217 -0
  90. package/src/md/menu/internal/SubMenu.styles.ts +8 -0
  91. package/src/md/menu/internal/SubMenu.ts +179 -0
  92. package/src/md/menu/ui-menu-item.ts +25 -0
  93. package/src/md/menu/ui-menu.ts +26 -0
  94. package/src/md/menu/ui-sub-menu.ts +25 -0
  95. package/src/mixins/FileDropMixin.ts +106 -107
  96. package/src/mixins/RenderableMixin.ts +107 -108
  97. package/test/lib/Dom.test.ts +231 -0
  98. package/test/md/menu/Menu.test.ts +509 -0
  99. package/test/md/menu/MenuIntegration.test.ts +426 -0
  100. package/test/md/menu/MenuItem.test.ts +361 -0
  101. package/test/md/menu/SubMenu.test.ts +411 -0
  102. package/web-test-runner.config.js +1 -1
  103. /package/test/{ui → md}/button/UiButton.test.ts +0 -0
  104. /package/test/{ui → md}/button/UiIconButton.test.ts +0 -0
  105. /package/test/{ui → md}/chip/UiChip.test.ts +0 -0
  106. /package/test/{ui → md}/collapse/UiCollapse.test.ts +0 -0
  107. /package/test/{ui → md}/collapse/flex-layout.test.ts +0 -0
  108. /package/test/{ui → md}/date-time/DateTime.test.ts +0 -0
  109. /package/test/{ui → md}/dialog/UiDialog.test.ts +0 -0
  110. /package/test/{ui → md}/progress/UiProgressElement.test.ts +0 -0
  111. /package/test/{ui → md}/progress/UiRangeElement.test.ts +0 -0
@@ -0,0 +1,19 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Material Design 3 Menu Component Demo</title>
7
+ <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0">
8
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
9
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet" />
10
+ <link href="../../../src/styles/m3/tokens.css" rel="stylesheet" type="text/css" />
11
+ <link href="../../../src/styles/m3/theme.css" rel="stylesheet" type="text/css" />
12
+ <link href="../../../src/styles/m3/native.css" rel="stylesheet" type="text/css" />
13
+
14
+ </head>
15
+ <body>
16
+ <div id="app"></div>
17
+ <script type="module" src="/.tmp/demo/md/menu/index.js"></script>
18
+ </body>
19
+ </html>
@@ -0,0 +1,154 @@
1
+ import { html, TemplateResult } from 'lit'
2
+ import { DemoPage } from '../../../src/demo/DemoPage.js'
3
+ import reactive from '../../../src/decorators/reactive.js'
4
+
5
+ import '../../../src/md/menu/ui-menu.js'
6
+ import '../../../src/md/menu/ui-sub-menu.js'
7
+ import '../../../src/md/menu/ui-menu-item.js'
8
+ import '../../../src/md/button/ui-button.js'
9
+ import '../../../src/md/icons/ui-icon.js'
10
+
11
+ class ComponentDemoPage extends DemoPage {
12
+ override accessor componentName = 'UI menu'
13
+
14
+ @reactive() accessor basicMenuOutput = ''
15
+ @reactive() accessor basicCount = 0
16
+ @reactive() accessor nestedMenuOutput = ''
17
+ @reactive() accessor nestedCount = 0
18
+
19
+ handleBasicMenuSelect(e: CustomEvent): void {
20
+ const item = e.detail.item as HTMLElement
21
+ const index = e.detail.index
22
+ this.basicMenuOutput = `Selected item: ${item.textContent}, Index: ${index}`
23
+ this.basicCount++
24
+ }
25
+
26
+ handleNestedMenuSelect(e: CustomEvent): void {
27
+ const item = e.detail.item as HTMLElement
28
+ const index = e.detail.index
29
+ this.nestedMenuOutput = `Selected item: ${item.textContent}, Index: ${index}`
30
+ this.nestedCount++
31
+ }
32
+
33
+ contentTemplate(): TemplateResult {
34
+ const { basicMenuOutput, nestedMenuOutput, nestedCount, basicCount } = this
35
+ return html`
36
+ <a href="../">Back</a>
37
+
38
+ <section class="demo-section">
39
+ <h2 class="title-large">Basic Menu</h2>
40
+ <p>Click the button below to open a basic menu:</p>
41
+ <ui-button id="basic-menu-trigger" color="filled" popovertarget="basic-menu">Open Basic Menu</ui-button>
42
+ <ui-menu id="basic-menu" popover="auto" @select="${this.handleBasicMenuSelect}">
43
+ <ui-menu-item>
44
+ <span slot="start"><ui-icon>add</ui-icon></span>
45
+ <span>New</span>
46
+ </ui-menu-item>
47
+ <ui-menu-item>
48
+ <span slot="start"><ui-icon>folder</ui-icon></span>
49
+ <span>Open</span>
50
+ </ui-menu-item>
51
+ <ui-menu-item>
52
+ <span slot="start"><ui-icon>save</ui-icon></span>
53
+ <span>Save</span>
54
+ </ui-menu-item>
55
+ <div class="menu-divider" role="separator"></div>
56
+ <ui-menu-item>
57
+ <span slot="start"><ui-icon>print</ui-icon></span>
58
+ <span>Print</span>
59
+ </ui-menu-item>
60
+ <ui-menu-item>
61
+ <span slot="start"><ui-icon>file_export</ui-icon></span>
62
+ <span>Export</span>
63
+ </ui-menu-item>
64
+ </ui-menu>
65
+ ${basicMenuOutput
66
+ ? html`<p>${basicMenuOutput}</p>
67
+ <p>Count: ${basicCount}</p>`
68
+ : ''}
69
+ </section>
70
+
71
+ <section class="demo-section">
72
+ <h2 class="title-large">Submenus</h2>
73
+ <p>A menu with submenus:</p>
74
+ <ui-button id="submenu-trigger" color="filled" popovertarget="submenu-demo"
75
+ >Open Attribute-based Menu</ui-button
76
+ >
77
+
78
+ <!-- Main Menu -->
79
+ <ui-menu id="submenu-demo" popover="auto" @select="${this.handleNestedMenuSelect}">
80
+ <ui-menu-item id="file-item" submenu="file-submenu">
81
+ <span slot="start"><ui-icon>docs</ui-icon></span>
82
+ <span>File</span>
83
+ </ui-menu-item>
84
+ <ui-menu-item id="edit-item" submenu="edit-submenu">
85
+ <span slot="start"><ui-icon>edit</ui-icon></span>
86
+ <span>Edit</span>
87
+ </ui-menu-item>
88
+ <ui-menu-item id="view-item">
89
+ <span slot="start"><ui-icon>visibility</ui-icon></span>
90
+ <span>View</span>
91
+ </ui-menu-item>
92
+ <!-- File Submenu (separate from main menu) -->
93
+ <ui-sub-menu id="file-submenu" anchor="file-item" popover="auto">
94
+ <ui-menu-item>
95
+ <span slot="start"><ui-icon>add</ui-icon></span>
96
+ <span>New File</span>
97
+ </ui-menu-item>
98
+ <ui-menu-item>
99
+ <span slot="start"><ui-icon>folder</ui-icon></span>
100
+ <span>Open File</span>
101
+ </ui-menu-item>
102
+ <ui-menu-item>
103
+ <span slot="start"><ui-icon>save</ui-icon></span>
104
+ <span>Save File</span>
105
+ </ui-menu-item>
106
+ <ui-menu-item id="export-item" submenu="export-submenu">
107
+ <span slot="start"><ui-icon>file_export</ui-icon></span>
108
+ <span>Export</span>
109
+ </ui-menu-item>
110
+
111
+ <!-- Nested Export Submenu -->
112
+ <ui-sub-menu id="export-submenu" anchor="export-item" popover="auto">
113
+ <ui-menu-item>
114
+ <span>Export as PDF</span>
115
+ </ui-menu-item>
116
+ <ui-menu-item>
117
+ <span>Export as PNG</span>
118
+ </ui-menu-item>
119
+ <ui-menu-item>
120
+ <span>Export as SVG</span>
121
+ </ui-menu-item>
122
+ </ui-sub-menu>
123
+ </ui-sub-menu>
124
+ <!-- Edit Submenu -->
125
+ <ui-sub-menu id="edit-submenu" anchor="edit-item" popover="auto">
126
+ <ui-menu-item>
127
+ <span slot="start"><ui-icon>undo</ui-icon></span>
128
+ <span>Undo</span>
129
+ </ui-menu-item>
130
+ <ui-menu-item>
131
+ <span slot="start"><ui-icon>redo</ui-icon></span>
132
+ <span>Redo</span>
133
+ </ui-menu-item>
134
+ <ui-menu-item>
135
+ <span slot="start"><ui-icon>cut</ui-icon></span>
136
+ <span>Cut</span>
137
+ </ui-menu-item>
138
+ <ui-menu-item>
139
+ <span slot="start"><ui-icon>content_copy</ui-icon></span>
140
+ <span>Copy</span>
141
+ </ui-menu-item>
142
+ </ui-sub-menu>
143
+ </ui-menu>
144
+ ${nestedMenuOutput
145
+ ? html`<p>${nestedMenuOutput}</p>
146
+ <p>Count: ${nestedCount}</p>`
147
+ : ''}
148
+ </section>
149
+ `
150
+ }
151
+ }
152
+
153
+ const instance = new ComponentDemoPage()
154
+ instance.render()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@api-client/ui",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Internal UI component library for the API Client ecosystem.",
5
5
  "license": "UNLICENSED",
6
6
  "main": "build/src/index.js",
@@ -173,12 +173,11 @@
173
173
  }
174
174
  },
175
175
  "dependencies": {
176
- "@api-client/core": "^0.16.0",
176
+ "@api-client/core": "^0.17.3",
177
177
  "@api-client/graph": "^0.3.6",
178
178
  "@api-client/json": "^0.2.0",
179
179
  "@github/relative-time-element": "^4.4.6",
180
180
  "@material/web": "^2.3.0",
181
- "@open-wc/dedupe-mixin": "^1.3.0",
182
181
  "@types/har-format": "^1.2.8",
183
182
  "dompurify": "^3.2.5",
184
183
  "idb-keyval": "^6.1.0",
package/src/index.ts CHANGED
@@ -4,6 +4,11 @@ export { default as MarkedHighlightElement } from './elements/highlight/MarkedHi
4
4
  export { default as PrismHighlightElement } from './elements/highlight/PrismHighlight.js'
5
5
  export { default as HarViewerElement } from './elements/har/HarViewer.js'
6
6
 
7
+ // Menu Components
8
+ export { default as Menu } from './md/menu/internal/Menu.js'
9
+ export { default as MenuItem } from './md/menu/internal/MenuItem.js'
10
+ export { default as SubMenu } from './md/menu/internal/SubMenu.js'
11
+
7
12
  // Demo
8
13
  export { DemoPage } from './demo/DemoPage.js'
9
14
 
package/src/lib/Dom.ts ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Searches for an element with the given ID in parent shadow roots
3
+ */
4
+ export function findElementInShadowRoots(id: string, from: HTMLElement): HTMLElement | null {
5
+ // First try to find in document
6
+ const rootElm = document.getElementById(id)
7
+ if (rootElm) {
8
+ return rootElm
9
+ }
10
+
11
+ // If not found in document, search in parent shadow roots
12
+ let current: HTMLElement | null = from
13
+ const queryId = `#${id}`
14
+
15
+ while (current) {
16
+ // Check if current element has a shadow root and search within it
17
+ if (current.shadowRoot) {
18
+ const element = current.shadowRoot.querySelector(queryId)
19
+ if (element) {
20
+ return element as HTMLElement
21
+ }
22
+ }
23
+
24
+ // Move up the DOM tree
25
+ if (current.parentNode) {
26
+ if (current.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
27
+ // We're inside a shadow root, move to the shadow host
28
+ const shadowRoot = current.parentNode as ShadowRoot
29
+ current = shadowRoot.host as HTMLElement
30
+ } else {
31
+ // Regular DOM traversal
32
+ current = current.parentElement
33
+ }
34
+ } else {
35
+ // No more parents
36
+ current = null
37
+ }
38
+ }
39
+
40
+ return null
41
+ }
@@ -1,9 +1,11 @@
1
1
  import { html, nothing, type PropertyValues, type TemplateResult } from 'lit'
2
2
  import { property, query } from 'lit/decorators.js'
3
+ import { nanoid } from 'nanoid'
3
4
  import { UiElement } from '../../UiElement.js'
4
5
  import type { BeginPressConfig, EndPressConfig } from '../../../controllers/ActionController.js'
5
6
  import { isDisabled, setDisabled } from '../../../lib/disabled.js'
6
7
  import type UiRipple from '../../ripple/internals/ripple.js'
8
+ import { findElementInShadowRoots } from '../../../lib/Dom.js'
7
9
 
8
10
  import '../../ripple/ui-ripple.js'
9
11
  import '@material/web/focus/md-focus-ring.js'
@@ -126,6 +128,26 @@ export default class BaseButton extends UiElement {
126
128
 
127
129
  @query('ui-ripple') protected accessor ripple!: UiRipple | null
128
130
 
131
+ /**
132
+ * Turns a `<ui-button>` element into a popover control button; takes the ID
133
+ * of the popover element to control as its value.
134
+ * @attribute
135
+ */
136
+ @property({ type: String, reflect: true }) accessor popoverTarget: string | undefined
137
+ /**
138
+ * Specifies the action to be performed on a popover element being controlled
139
+ * by a control <ui-button>. Possible values are:
140
+ *
141
+ * - `hide` - The button will hide a shown popover. If you try to hide an already hidden popover,
142
+ * no action will be taken.
143
+ * - `show` - The button will show a hidden popover. If you try to show an already showing popover,
144
+ * no action will be taken.
145
+ * - `toggle` - The button will toggle a popover between showing and hidden. If the popover is hidden,
146
+ * it will be shown; if the popover is showing, it will be hidden. If popoverTargetAction is omitted,
147
+ * "toggle" is the default action that will be performed by the control button.
148
+ */
149
+ @property({ type: String, reflect: true }) accessor popoverTargetAction: string | undefined
150
+
129
151
  constructor() {
130
152
  super()
131
153
  this.actionController.cancelKeyboardEvents = true
@@ -150,6 +172,13 @@ export default class BaseButton extends UiElement {
150
172
  }
151
173
  }
152
174
 
175
+ protected override update(changedProperties: PropertyValues<this>): void {
176
+ if (changedProperties.has('popoverTarget')) {
177
+ this.setupPopoverTarget()
178
+ }
179
+ super.update(changedProperties)
180
+ }
181
+
153
182
  protected override updated(cp: PropertyValues<this>): void {
154
183
  super.updated(cp)
155
184
 
@@ -228,9 +257,57 @@ export default class BaseButton extends UiElement {
228
257
  })
229
258
  this.dispatchEvent(e)
230
259
  }
260
+ this.handlePopoverAction()
231
261
  this.endPress({ cancelled: false, actionData: { event: e } })
232
262
  }
233
263
 
264
+ protected setupPopoverTarget(): void {
265
+ const id = this.popoverTarget
266
+ if (!id) {
267
+ return
268
+ }
269
+ const element = findElementInShadowRoots(id, this)
270
+ if (!element) {
271
+ return
272
+ }
273
+ // A regular button first sets its value to the popover target ID,
274
+ this.value = id
275
+ // create an anchor association with the popover target
276
+ if (!this.id) {
277
+ this.id = nanoid()
278
+ }
279
+ const anchorName = `--anchor-${this.id}`
280
+ this.style.setProperty('anchor-name', anchorName)
281
+ element.style.setProperty('position-anchor', anchorName)
282
+ this.setAttribute('aria-details', id)
283
+ this.setAttribute('aria-controls', id)
284
+ }
285
+
286
+ /**
287
+ * When the button has a popover target, this method toggles the popover
288
+ * visibility by finding the element in the shadow roots and calling its
289
+ * `togglePopover` method.
290
+ */
291
+ protected handlePopoverAction(): void {
292
+ const id = this.popoverTarget
293
+ if (!id) {
294
+ return
295
+ }
296
+ const element = findElementInShadowRoots(id, this)
297
+ if (!element) {
298
+ return
299
+ }
300
+ const action = this.popoverTargetAction || 'toggle'
301
+ if (action === 'hide') {
302
+ element.hidePopover()
303
+ } else if (action === 'show') {
304
+ element.showPopover()
305
+ } else {
306
+ element.togglePopover()
307
+ }
308
+ element.focus()
309
+ }
310
+
234
311
  protected handleSubmit(): void {
235
312
  const { name, value, type, disabled, form } = this
236
313
  if (!form || !type || disabled) {
@@ -62,10 +62,10 @@ export default class UiIcon extends LitElement {
62
62
  * @return Template result for an icon
63
63
  */
64
64
  override render(): TemplateResult {
65
- const { hasIcon, iconValue: _iconValue } = this
65
+ const { hasIcon, iconValue } = this
66
66
  if (!hasIcon) {
67
67
  return html`<slot></slot>`
68
68
  }
69
- return html`${_iconValue}`
69
+ return html`${iconValue}`
70
70
  }
71
71
  }
@@ -354,16 +354,16 @@ export default class UiList extends LitElement {
354
354
  * @param item The UiListItem that is selected.
355
355
  * @returns True when the event was canceled.
356
356
  */
357
- notifySelect(item: UiListItem): boolean {
358
- const index = this.items.indexOf(item)
359
- if (index === -1) {
357
+ notifySelect(item: UiListItem, index?: number): boolean {
358
+ const resolvedIndex = index ?? this.items.indexOf(item)
359
+ if (resolvedIndex === -1) {
360
360
  return false
361
361
  }
362
362
  const event = new CustomEvent<UiListSelection>('select', {
363
363
  cancelable: true,
364
364
  detail: {
365
365
  item,
366
- index,
366
+ index: resolvedIndex,
367
367
  },
368
368
  })
369
369
  this.dispatchEvent(event)
@@ -34,26 +34,8 @@ export default css`
34
34
 
35
35
  .surface {
36
36
  height: inherit;
37
- position: relative;
38
-
39
- display: flex;
40
- align-items: center;
41
- }
42
-
43
- .surface,
44
- .content {
45
- width: 100%;
46
- }
37
+ box-sizing: border-box;
47
38
 
48
- .container {
49
- position: absolute;
50
- inset: 0;
51
- z-index: 1;
52
- }
53
-
54
- .content {
55
- position: relative;
56
- z-index: 2;
57
39
  display: flex;
58
40
  align-items: center;
59
41
  overflow: hidden;
@@ -64,7 +46,7 @@ export default css`
64
46
  z-index: 3;
65
47
  }
66
48
 
67
- :host([image='video']) .content {
49
+ :host([image='video']) .surface {
68
50
  /* The spec says the right padding is 24px but counting from the checkbox check border.
69
51
  This does not include spacing presented by the checkbox itself (18 box, 40px state layer)
70
52
  */
@@ -5,7 +5,6 @@ import { UiElement } from '../../UiElement.js'
5
5
  import { BeginPressConfig, EndPressConfig } from '../../../controllers/ActionController.js'
6
6
  import UiRipple from '../../ripple/internals/ripple.js'
7
7
  import { setDisabled } from '../../../lib/disabled.js'
8
- import { ripple } from '../../effects/rippleDirective.js'
9
8
 
10
9
  import '@material/web/focus/md-focus-ring.js'
11
10
  import '../../ripple/ui-ripple.js'
@@ -196,20 +195,12 @@ export default class UiListItem extends UiElement {
196
195
  }
197
196
 
198
197
  protected override render(): TemplateResult {
199
- const { pressed = false } = this
200
198
  const surfaceClasses = classMap({
201
199
  surface: true,
202
- pressed,
203
200
  })
204
-
205
201
  return html`
206
- <md-focus-ring part="focus-ring" .control="${this as HTMLElement}" inward></md-focus-ring>
207
- <div class="${surfaceClasses}" ${ripple(() => this.ripple)}>
208
- <div class="container"></div>
209
- <div class="state"></div>
210
- <div class="content">${this.renderStart()} ${this.renderBody()} ${this.renderEnd()}</div>
211
- ${this.renderRipple()}
212
- </div>
202
+ ${this.renderFocusRing()} ${this.renderRipple()}
203
+ <div class="${surfaceClasses}">${this.renderStart()} ${this.renderBody()} ${this.renderEnd()}</div>
213
204
  `
214
205
  }
215
206
 
@@ -218,6 +209,15 @@ export default class UiListItem extends UiElement {
218
209
  return html`<ui-ripple class="ripple" ?disabled="${disabled || isStatic}"></ui-ripple>`
219
210
  }
220
211
 
212
+ protected renderFocusRing(): TemplateResult {
213
+ return html`<md-focus-ring
214
+ part="focus-ring"
215
+ class="focus-ring"
216
+ .control="${this as HTMLElement}"
217
+ inward
218
+ ></md-focus-ring>`
219
+ }
220
+
221
221
  protected renderStart(): TemplateResult {
222
222
  return html`<div class="start">
223
223
  <slot name="start" @slotchange=${this.handleSlotChange}></slot>