@api-client/ui 0.5.20 → 0.5.22
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 +13 -36
- package/build/src/md/dialog/internals/Dialog.d.ts.map +1 -1
- package/build/src/md/dialog/internals/Dialog.js +32 -146
- 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 +30 -0
- package/build/src/md/menu/internal/Menu.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 +107 -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 +20 -122
- 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.ts +33 -0
- 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/build/src/events/SyntheticSubmitEvent.d.ts +0 -9
- package/build/src/events/SyntheticSubmitEvent.d.ts.map +0 -1
- package/build/src/events/SyntheticSubmitEvent.js +0 -22
- package/build/src/events/SyntheticSubmitEvent.js.map +0 -1
- package/src/events/SyntheticSubmitEvent.ts +0 -27
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { html, nothing, type PropertyValues, type TemplateResult } from 'lit'
|
|
2
2
|
import { property, query, queryAssignedElements, queryAssignedNodes, state } from 'lit/decorators.js'
|
|
3
3
|
import { type ClassInfo, classMap } from 'lit/directives/class-map.js'
|
|
4
4
|
import { UiElement } from '../../UiElement.js'
|
|
5
5
|
import { isDisabled, setDisabled } from '../../../lib/disabled.js'
|
|
6
6
|
import type UiButton from '../../button/internals/button.js'
|
|
7
7
|
import type { TypedEvents } from '../../../core/types.js'
|
|
8
|
-
import {
|
|
9
|
-
import { SyntheticSubmitEvent } from '../../../events/SyntheticSubmitEvent.js'
|
|
8
|
+
import type { ButtonType } from '../../button/internals/button.js'
|
|
10
9
|
import '../../button/ui-button.js'
|
|
11
10
|
import { bound } from '../../../decorators/bound.js'
|
|
12
11
|
|
|
@@ -28,8 +27,6 @@ interface DialogEventMap {
|
|
|
28
27
|
close: CustomEvent<UiDialogClosingReason>
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
export type RenderFunction = () => TemplateResult
|
|
32
|
-
|
|
33
30
|
/**
|
|
34
31
|
* Styled dialog using a native `<dialog>` element under the hood.
|
|
35
32
|
* Note, since native dialog renders in the top layer it is not necessary
|
|
@@ -42,8 +39,8 @@ export type RenderFunction = () => TemplateResult
|
|
|
42
39
|
* closing reason detail.
|
|
43
40
|
*
|
|
44
41
|
* ```javascript
|
|
45
|
-
* <ui-button color="text" value="dismiss">Cancel</ui-button>
|
|
46
|
-
* <ui-button color="text" value="confirm">Take action</ui-button>
|
|
42
|
+
* <ui-button slot="button" color="text" value="dismiss">Cancel</ui-button>
|
|
43
|
+
* <ui-button slot="button" color="text" value="confirm">Take action</ui-button>
|
|
47
44
|
* ```
|
|
48
45
|
*
|
|
49
46
|
* ```javascript
|
|
@@ -133,26 +130,6 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
|
|
|
133
130
|
*/
|
|
134
131
|
@property({ type: Boolean }) accessor destructive = false
|
|
135
132
|
|
|
136
|
-
/**
|
|
137
|
-
* Part of the imperative access to the element.
|
|
138
|
-
* When set, it wraps the content in a `<form>` element.
|
|
139
|
-
* When this is enabled the following will happen:
|
|
140
|
-
* - The `confirm` button will get `type="submit"`
|
|
141
|
-
* - The form `method` attribute is set to `dialog`
|
|
142
|
-
* - The form `id` attribute is set to a random string. You can get it from the `formId` getter.
|
|
143
|
-
* - The dialog will dispatch the same `submit` event as the form.
|
|
144
|
-
* - When the `submit` event dispatched by the dialog gets cancelled, then:
|
|
145
|
-
* - The original submit event also gets cancelled.
|
|
146
|
-
* - The default confirm action is not invoked
|
|
147
|
-
* - The dialog stays opened
|
|
148
|
-
* - When the submit event is not cancelled, then:
|
|
149
|
-
* - The default confirm action is invoked.
|
|
150
|
-
* - The dialog is closed.
|
|
151
|
-
*
|
|
152
|
-
* @deprecated Wrap the content in a `<form>` element instead.
|
|
153
|
-
*/
|
|
154
|
-
@property({ type: Boolean }) accessor useForm: boolean | undefined
|
|
155
|
-
|
|
156
133
|
/**
|
|
157
134
|
* Only when `confirmLabel` is set.
|
|
158
135
|
* Defines the value associated with the button's name when it's submitted with the form data.
|
|
@@ -160,6 +137,15 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
|
|
|
160
137
|
* @attribute
|
|
161
138
|
*/
|
|
162
139
|
@property({ type: String }) accessor confirmValue: string | undefined
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The type of the confirm button.
|
|
143
|
+
* This is used to set the button's `type` attribute.
|
|
144
|
+
* If not set, the default is `button`.
|
|
145
|
+
* If set to `submit`, the dialog will trigger the form submission on the parent form.
|
|
146
|
+
* @attribute
|
|
147
|
+
*/
|
|
148
|
+
@property({ type: String }) accessor confirmType: ButtonType | undefined
|
|
163
149
|
/**
|
|
164
150
|
* When the dialog is wrapped in a form, set this to `true` to close the dialog
|
|
165
151
|
* when the form is submitted.
|
|
@@ -167,6 +153,7 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
|
|
|
167
153
|
* Note that the dialog doesn't perform any validation of the form. It only closes
|
|
168
154
|
* when the form is submitted, regardless of the application logic. The `submit` event
|
|
169
155
|
* is dispatched by the dialog when the form is valid.
|
|
156
|
+
* @attribute
|
|
170
157
|
*/
|
|
171
158
|
@property({ type: Boolean }) accessor submitClose: boolean | undefined
|
|
172
159
|
|
|
@@ -193,25 +180,6 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
|
|
|
193
180
|
*/
|
|
194
181
|
dialogValue?: unknown
|
|
195
182
|
|
|
196
|
-
#formId?: string
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* @deprecated Use `useForm` instead.
|
|
200
|
-
*/
|
|
201
|
-
get formId(): string | undefined {
|
|
202
|
-
return this.#formId
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* @deprecated This will be removed in the future.
|
|
207
|
-
*/
|
|
208
|
-
@state() accessor #inject: TemplateResult | RenderFunction | undefined
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* @deprecated This will be removed in the future.
|
|
212
|
-
*/
|
|
213
|
-
#pendingStyles?: CSSResultOrNative[]
|
|
214
|
-
|
|
215
183
|
/**
|
|
216
184
|
* A reference to the parent form element.
|
|
217
185
|
* When a form is found, the dialog will hook into the form's submit event
|
|
@@ -228,29 +196,6 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
|
|
|
228
196
|
this.addEventListener('keydown', this.handleKeyDown)
|
|
229
197
|
}
|
|
230
198
|
|
|
231
|
-
/**
|
|
232
|
-
* Allows to inject a template into the internals of the element.
|
|
233
|
-
* This is helpful when working with imperative dialogs.
|
|
234
|
-
* @param content The content to inject into the body.
|
|
235
|
-
* @deprecated This will be removed in the future. To use forms, wrap the content in a `<form>` element.
|
|
236
|
-
*/
|
|
237
|
-
inject(content?: TemplateResult | RenderFunction, styles?: CSSResultOrNative[]): void {
|
|
238
|
-
this.#inject = content
|
|
239
|
-
if (styles) {
|
|
240
|
-
if (this.shadowRoot) {
|
|
241
|
-
adoptStyles(this.shadowRoot, styles)
|
|
242
|
-
} else {
|
|
243
|
-
if (!this.#pendingStyles) {
|
|
244
|
-
this.#pendingStyles = []
|
|
245
|
-
}
|
|
246
|
-
this.#pendingStyles.push(...styles)
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
if (this.shadowRoot && styles) {
|
|
250
|
-
adoptStyles(this.shadowRoot, styles)
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
199
|
override connectedCallback(): void {
|
|
255
200
|
super.connectedCallback()
|
|
256
201
|
this.#form = this.closest('form')
|
|
@@ -267,22 +212,6 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
|
|
|
267
212
|
}
|
|
268
213
|
}
|
|
269
214
|
|
|
270
|
-
protected override firstUpdated(): void {
|
|
271
|
-
const styles = this.#pendingStyles
|
|
272
|
-
if (styles) {
|
|
273
|
-
const root = this.shadowRoot as ShadowRoot
|
|
274
|
-
const all = [...root.adoptedStyleSheets, ...styles]
|
|
275
|
-
adoptStyles(this.shadowRoot as ShadowRoot, all)
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
override willUpdate(cp: PropertyValues<this>): void {
|
|
280
|
-
if (cp.has('useForm') && this.useForm && !this.#formId) {
|
|
281
|
-
const r = (Math.random() + 1).toString(36).substring(7)
|
|
282
|
-
this.#formId = `form-${r}`
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
215
|
@bound
|
|
287
216
|
protected handleFormSubmit(): void {
|
|
288
217
|
if (this.submitClose) {
|
|
@@ -425,33 +354,15 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
|
|
|
425
354
|
}
|
|
426
355
|
|
|
427
356
|
protected handleConfirm(): void {
|
|
428
|
-
|
|
429
|
-
this.handleInteraction('confirm')
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
#handleSubmit(event: SubmitEvent): void {
|
|
434
|
-
const form = event.target as HTMLFormElement
|
|
435
|
-
const copy = new SyntheticSubmitEvent(event.type, form, {
|
|
436
|
-
submitter: event.submitter,
|
|
437
|
-
cancelable: true,
|
|
438
|
-
bubbles: false,
|
|
439
|
-
composed: false,
|
|
440
|
-
})
|
|
441
|
-
const dispatched = this.dispatchEvent(copy)
|
|
442
|
-
if (dispatched) {
|
|
443
|
-
this.handleInteraction('confirm')
|
|
444
|
-
} else {
|
|
445
|
-
event.preventDefault()
|
|
446
|
-
}
|
|
357
|
+
this.handleInteraction('confirm')
|
|
447
358
|
}
|
|
448
359
|
|
|
449
360
|
override render(): TemplateResult {
|
|
450
|
-
const {
|
|
361
|
+
const { modal } = this
|
|
451
362
|
const dialogClass = modal ? 'modal' : 'non-modal'
|
|
452
363
|
return html`
|
|
453
364
|
<dialog @close="${this.handleDialogClose}" part="dialog" class="${dialogClass}">
|
|
454
|
-
<div class="container">${
|
|
365
|
+
<div class="container">${this.renderContent()}</div>
|
|
455
366
|
</dialog>
|
|
456
367
|
`
|
|
457
368
|
}
|
|
@@ -460,14 +371,6 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
|
|
|
460
371
|
return [this.renderIcon(), this.renderTitle(), this.renderBody(), this.renderButtons()]
|
|
461
372
|
}
|
|
462
373
|
|
|
463
|
-
#renderFormContent(): TemplateResult {
|
|
464
|
-
return html`
|
|
465
|
-
<form id="${ifDefined(this.formId)}" method="dialog" @submit="${this.#handleSubmit}" part="form">
|
|
466
|
-
${this.renderContent()}
|
|
467
|
-
</form>
|
|
468
|
-
`
|
|
469
|
-
}
|
|
470
|
-
|
|
471
374
|
protected renderIcon(): TemplateResult {
|
|
472
375
|
const classes: ClassInfo = {
|
|
473
376
|
'icon': true,
|
|
@@ -494,12 +397,7 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
|
|
|
494
397
|
}
|
|
495
398
|
|
|
496
399
|
protected renderBody(): TemplateResult {
|
|
497
|
-
|
|
498
|
-
if (typeof content === 'function') {
|
|
499
|
-
content = content()
|
|
500
|
-
}
|
|
501
|
-
const injected = content || nothing
|
|
502
|
-
return html` <div class="content" part="body"><slot></slot>${injected}</div> `
|
|
400
|
+
return html` <div class="content" part="body"><slot></slot></div> `
|
|
503
401
|
}
|
|
504
402
|
|
|
505
403
|
protected renderButtons(): TemplateResult {
|
|
@@ -533,11 +431,11 @@ export default class UiDialog extends UiElement implements TypedEvents<DialogEve
|
|
|
533
431
|
}
|
|
534
432
|
|
|
535
433
|
#renderConfirmButton(): TemplateResult | typeof nothing {
|
|
536
|
-
const { confirmLabel, confirmValue = 'confirm',
|
|
434
|
+
const { confirmLabel, confirmValue = 'confirm', destructive } = this
|
|
537
435
|
if (!confirmLabel) {
|
|
538
436
|
return nothing
|
|
539
437
|
}
|
|
540
|
-
const type =
|
|
438
|
+
const type = this.confirmType ?? 'button'
|
|
541
439
|
const buttonClass = destructive ? 'internal-button destructive' : 'internal-button'
|
|
542
440
|
return html`
|
|
543
441
|
<ui-button
|
|
@@ -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
|
@@ -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
|
/**
|
|
@@ -213,10 +214,42 @@ export default class Menu extends UiList {
|
|
|
213
214
|
}
|
|
214
215
|
|
|
215
216
|
override notifySelect(item: UiListItem, index?: number): boolean {
|
|
217
|
+
// Handle single selection
|
|
218
|
+
if (item instanceof UiMenuItem) {
|
|
219
|
+
this.clearSelection()
|
|
220
|
+
item.selected = true
|
|
221
|
+
}
|
|
222
|
+
|
|
216
223
|
this.hide()
|
|
217
224
|
return super.notifySelect(item, index)
|
|
218
225
|
}
|
|
219
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Clears selection from all menu items
|
|
229
|
+
*/
|
|
230
|
+
protected clearSelection(): void {
|
|
231
|
+
this.assignedMenuItems.forEach((menuItem) => {
|
|
232
|
+
menuItem.selected = false
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Gets the currently selected menu item
|
|
238
|
+
*/
|
|
239
|
+
get selectedItem(): UiMenuItem | null {
|
|
240
|
+
return this.assignedMenuItems.find((item) => item.selected) || null
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Sets the selected menu item
|
|
245
|
+
*/
|
|
246
|
+
setSelectedItem(item: UiMenuItem | null): void {
|
|
247
|
+
this.clearSelection()
|
|
248
|
+
if (item) {
|
|
249
|
+
item.selected = true
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
220
253
|
/**
|
|
221
254
|
* Handles sub-menu opening
|
|
222
255
|
*/
|
|
@@ -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