Skip to content

Commit 3c0faec

Browse files
authored
Merge pull request #3589 from material-components/refactor-select-menu
2 parents 1f5fc52 + 88b308c commit 3c0faec

File tree

3 files changed

+122
-65
lines changed

3 files changed

+122
-65
lines changed

packages/menu/mwc-menu-base.ts

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ import {DefaultFocusState as DefaultFocusStateEnum} from '@material/menu/constan
1515
import MDCMenuFoundation from '@material/menu/foundation.js';
1616
import {BaseElement} from '@material/mwc-base/base-element.js';
1717
import {observer} from '@material/mwc-base/observer.js';
18-
import {List, MWCListIndex} from '@material/mwc-list/mwc-list.js';
1918
import {ActionDetail} from '@material/mwc-list/mwc-list-foundation.js';
2019
import {ListItemBase} from '@material/mwc-list/mwc-list-item-base.js';
20+
import {List, MWCListIndex} from '@material/mwc-list/mwc-list.js';
2121
import {html} from 'lit';
2222
import {property, query} from 'lit/decorators.js';
23+
import {classMap} from 'lit/directives/class-map.js';
2324

24-
import {MenuSurface} from './mwc-menu-surface.js';
2525
import {Corner, MenuCorner} from './mwc-menu-surface-base.js';
26+
import {MenuSurface} from './mwc-menu-surface.js';
2627

2728
export {ActionDetail, createSetFromIndex, isEventMulti, isIndexSet, MWCListIndex, SelectedDetail} from '@material/mwc-list/mwc-list-foundation.js';
2829
export {Corner, MenuCorner} from './mwc-menu-surface-base.js';
@@ -131,39 +132,63 @@ export abstract class MenuBase extends BaseElement {
131132
}
132133

133134
override render() {
134-
const itemRoles = this.innerRole === 'menu' ? 'menuitem' : 'option';
135+
return this.renderSurface();
136+
}
135137

138+
protected renderSurface() {
139+
const classes = this.getSurfaceClasses();
136140
return html`
137141
<mwc-menu-surface
138-
?hidden=${!this.open}
139-
.anchor=${this.anchor}
140-
.open=${this.open}
141-
.quick=${this.quick}
142-
.corner=${this.corner}
143-
.x=${this.x}
144-
.y=${this.y}
145-
.absolute=${this.absolute}
146-
.fixed=${this.fixed}
147-
.fullwidth=${this.fullwidth}
148-
.menuCorner=${this.menuCorner}
149-
?stayOpenOnBodyClick=${this.stayOpenOnBodyClick}
150-
class="mdc-menu mdc-menu-surface"
151-
@closed=${this.onClosed}
152-
@opened=${this.onOpened}
153-
@keydown=${this.onKeydown}>
154-
<mwc-list
142+
?hidden=${!this.open}
143+
.anchor=${this.anchor}
144+
.open=${this.open}
145+
.quick=${this.quick}
146+
.corner=${this.corner}
147+
.x=${this.x}
148+
.y=${this.y}
149+
.absolute=${this.absolute}
150+
.fixed=${this.fixed}
151+
.fullwidth=${this.fullwidth}
152+
.menuCorner=${this.menuCorner}
153+
?stayOpenOnBodyClick=${this.stayOpenOnBodyClick}
154+
class=${classMap(classes)}
155+
@closed=${this.onClosed}
156+
@opened=${this.onOpened}
157+
@keydown=${this.onKeydown}>
158+
${this.renderList()}
159+
</mwc-menu-surface>`;
160+
}
161+
162+
protected getSurfaceClasses() {
163+
return {
164+
'mdc-menu': true,
165+
'mdc-menu-surface': true,
166+
};
167+
}
168+
169+
protected renderList() {
170+
const itemRoles = this.innerRole === 'menu' ? 'menuitem' : 'option';
171+
const classes = this.renderListClasses();
172+
173+
return html`
174+
<mwc-list
155175
rootTabbable
156176
.innerAriaLabel=${this.innerAriaLabel}
157177
.innerRole=${this.innerRole}
158178
.multi=${this.multi}
159-
class="mdc-deprecated-list"
179+
class=${classMap(classes)}
160180
.itemRoles=${itemRoles}
161181
.wrapFocus=${this.wrapFocus}
162182
.activatable=${this.activatable}
163183
@action=${this.onAction}>
164184
<slot></slot>
165-
</mwc-list>
166-
</mwc-menu-surface>`;
185+
</mwc-list>`;
186+
}
187+
188+
protected renderListClasses() {
189+
return {
190+
'mdc-deprecated-list': true,
191+
};
167192
}
168193

169194
protected createAdapter(): MDCMenuAdapter {
@@ -263,7 +288,7 @@ export abstract class MenuBase extends BaseElement {
263288

264289
return -1;
265290
},
266-
notifySelected: () => { /** handled by list */ },
291+
notifySelected: () => {/** handled by list */},
267292
getMenuItemCount: () => {
268293
const listElement = this.listElement;
269294
if (!listElement) {

packages/menu/mwc-menu-surface-base.ts

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,7 @@ export abstract class MenuSurfaceBase extends BaseElement {
9898

9999
@property({type: Boolean, reflect: true})
100100
@observer(function(this: MenuSurfaceBase, isOpen: boolean, wasOpen: boolean) {
101-
if (this.mdcFoundation) {
102-
if (isOpen) {
103-
this.mdcFoundation.open();
104-
// wasOpen helps with first render (when it is `undefined`) perf
105-
} else if (wasOpen !== undefined) {
106-
this.mdcFoundation.close();
107-
}
108-
}
101+
this.onOpenChanged(isOpen, wasOpen);
109102
})
110103
open = false;
111104

@@ -173,29 +166,44 @@ export abstract class MenuSurfaceBase extends BaseElement {
173166
protected onBodyClickBound: (evt: MouseEvent) => void = () => undefined;
174167

175168
override render() {
176-
const classes = {
169+
return this.renderSurface();
170+
}
171+
172+
protected renderSurface() {
173+
const classes = this.getRootClasses();
174+
const styles = this.getRootStyles();
175+
return html`
176+
<div
177+
class=${classMap(classes)}
178+
style="${styleMap(styles)}"
179+
@keydown=${this.onKeydown}
180+
@opened=${this.registerBodyClick}
181+
@closed=${this.deregisterBodyClick}>
182+
${this.renderContent()}
183+
</div>`;
184+
}
185+
186+
protected getRootClasses() {
187+
return {
188+
'mdc-menu-surface': true,
177189
'mdc-menu-surface--fixed': this.fixed,
178190
'mdc-menu-surface--fullwidth': this.fullwidth,
179191
};
192+
}
180193

181-
const styles = {
194+
protected getRootStyles() {
195+
return {
182196
'top': this.styleTop,
183197
'left': this.styleLeft,
184198
'right': this.styleRight,
185199
'bottom': this.styleBottom,
186200
'max-height': this.styleMaxHeight,
187201
'transform-origin': this.styleTransformOrigin,
188202
};
203+
}
189204

190-
return html`
191-
<div
192-
class="mdc-menu-surface ${classMap(classes)}"
193-
style="${styleMap(styles)}"
194-
@keydown=${this.onKeydown}
195-
@opened=${this.registerBodyClick}
196-
@closed=${this.deregisterBodyClick}>
197-
<slot></slot>
198-
</div>`;
205+
protected renderContent() {
206+
return html`<slot></slot>`;
199207
}
200208

201209
createAdapter(): MDCMenuSurfaceAdapter {
@@ -351,6 +359,17 @@ export abstract class MenuSurfaceBase extends BaseElement {
351359
'click', this.onBodyClickBound, {capture: true});
352360
}
353361

362+
protected onOpenChanged(isOpen: boolean, wasOpen: boolean) {
363+
if (this.mdcFoundation) {
364+
if (isOpen) {
365+
this.mdcFoundation.open();
366+
// wasOpen helps with first render (when it is `undefined`) perf
367+
} else if (wasOpen !== undefined) {
368+
this.mdcFoundation.close();
369+
}
370+
}
371+
}
372+
354373
close() {
355374
this.open = false;
356375
}

packages/select/mwc-select-base.ts

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,6 @@ export abstract class SelectBase extends FormElement {
253253
'mdc-select--invalid': !this.isUiValid,
254254
};
255255

256-
const menuClasses = {
257-
'mdc-select__menu--invalid': !this.isUiValid,
258-
};
259-
260256
const labelledby = !!this.label ? 'label' : undefined;
261257
const describedby = this.shouldRenderHelperText ? 'helper-text' : undefined;
262258

@@ -311,28 +307,45 @@ export abstract class SelectBase extends FormElement {
311307
</span>
312308
${this.renderLineRipple()}
313309
</div>
314-
<mwc-menu
315-
innerRole="listbox"
316-
wrapFocus
317-
class="mdc-select__menu mdc-menu mdc-menu-surface ${
318-
classMap(menuClasses)}"
319-
activatable
320-
.fullwidth=${
321-
this.fixedMenuPosition ? false : !this.naturalMenuWidth}
322-
.open=${this.menuOpen}
323-
.anchor=${this.anchorElement}
324-
.fixed=${this.fixedMenuPosition}
325-
@selected=${this.onSelected}
326-
@opened=${this.onOpened}
327-
@closed=${this.onClosed}
328-
@items-updated=${this.onItemsUpdated}
329-
@keydown=${this.handleTypeahead}>
330-
<slot></slot>
331-
</mwc-menu>
310+
${this.renderMenu()}
332311
</div>
333312
${this.renderHelperText()}`;
334313
}
335314

315+
protected renderMenu() {
316+
const classes = this.getMenuClasses();
317+
return html`
318+
<mwc-menu
319+
innerRole="listbox"
320+
wrapFocus
321+
class=" ${classMap(classes)}"
322+
activatable
323+
.fullwidth=${this.fixedMenuPosition ? false : !this.naturalMenuWidth}
324+
.open=${this.menuOpen}
325+
.anchor=${this.anchorElement}
326+
.fixed=${this.fixedMenuPosition}
327+
@selected=${this.onSelected}
328+
@opened=${this.onOpened}
329+
@closed=${this.onClosed}
330+
@items-updated=${this.onItemsUpdated}
331+
@keydown=${this.handleTypeahead}>
332+
${this.renderMenuContent()}
333+
</mwc-menu>`;
334+
}
335+
336+
protected getMenuClasses() {
337+
return {
338+
'mdc-select__menu': true,
339+
'mdc-menu': true,
340+
'mdc-menu-surface': true,
341+
'mdc-select__menu--invalid': !this.isUiValid,
342+
};
343+
}
344+
345+
protected renderMenuContent() {
346+
return html`<slot></slot>`;
347+
}
348+
336349
protected renderRipple() {
337350
if (this.outlined) {
338351
return nothing;

0 commit comments

Comments
 (0)