Skip to content

Instantly share code, notes, and snippets.

@e111077
Last active April 23, 2023 17:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save e111077/f740bdff9ea4e62a64fae47bbdf5797e to your computer and use it in GitHub Desktop.
Save e111077/f740bdff9ea4e62a64fae47bbdf5797e to your computer and use it in GitHub Desktop.
MWC Select working inside an MWC dialog
<!DOCTYPE html>
<head>
<script type="module" src="./simple-greeting.js"></script>
</head>
<body>
<simple-greeting name="World"></simple-greeting>
</body>
import {MenuSurfaceBase} from '@material/mwc-menu/mwc-menu-surface-base.js';
import {styles} from '@material/mwc-menu/mwc-menu-surface.css.js';
import {html, css} from 'lit';
import {customElement, query} from 'lit/decorators.js';
import {classMap} from 'lit/directives/class-map.js';
import {styleMap} from 'lit/directives/style-map.js';
/** */
@customElement('my-menu-surface')
export class MenuSurface extends MenuSurfaceBase {
static override styles = [
styles,
css`
dialog {
border: 0px;
}
dialog::backdrop {
/* must still be clickable */
opacity: 0;
}
slot {
/* Makes slot clickable */
display: block;
}`
];
@query('dialog') dialogEl!: HTMLDialogElement | null;
override renderSurface() {
const classes = this.getRootClasses();
const styles = this.getRootStyles();
// swap out div for dialog
return html`
<dialog
class=${classMap(classes)}
style="${styleMap(styles)}"
@click=${this.onScrimClick}
@keydown=${this.onKeydown}
@opened=${this.registerBodyClick}
@closed=${this.deregisterBodyClick}>
${this.renderContent()}
</dialog>`;
}
protected onScrimClick(e: Event) {
// This would be false if the contents of the dialog were clicked rather
// than the transparent scrim
if (e.target === this.dialogEl) {
this.open = false;
}
}
override onOpenChanged(isOpen: boolean, wasOpen: boolean) {
super.onOpenChanged(isOpen, wasOpen);
if (this.dialogEl) {
if (isOpen) {
// MUST use showModal over `open` because this is the only thing that
// will trigger the browser's super special hoisting mechanism
this.dialogEl.showModal();
} else {
this.dialogEl.close();
}
}
}
}
declare global {
interface HTMLElementTagNameMap {
'my-menu-surface': MenuSurface;
}
}
import {MenuBase} from '@material/mwc-menu/mwc-menu-base.js';
import {styles} from '@material/mwc-menu/mwc-menu.css.js';
import {html} from 'lit';
import {customElement} from 'lit/decorators.js';
import {classMap} from 'lit/directives/class-map.js';
import './my-menu-surface.js';
/** */
@customElement('my-menu')
export class Menu extends MenuBase {
static styles = styles;
override renderSurface() {
const classes = this.getSurfaceClasses();
// swap out mwc-menu surface for custom implementation
// also force "fixed" behavior and turn off fullwidth since that won't work
// with dialog. Alternatively these changes to defaults can be set in
// my-menu-surface.ts
return html`
<my-menu-surface
?hidden=${!this.open}
.anchor=${this.anchor}
.open=${this.open}
.quick=${this.quick}
.corner=${this.corner}
.x=${this.x}
.y=${this.y}
.absolute=${this.absolute}
fixed
.menuCorner=${this.menuCorner}
?stayOpenOnBodyClick=${this.stayOpenOnBodyClick}
class=${classMap(classes)}
@closed=${this.onClosed}
@opened=${this.onOpened}
@keydown=${this.onKeydown}>
${this.renderList()}
</my-menu-surface>`;
}
}
declare global {
interface HTMLElementTagNameMap {
'my-menu': Menu;
}
}
import {SelectBase} from '@material/mwc-select/mwc-select-base.js';
import {styles} from '@material/mwc-select/mwc-select.css.js';
import {html} from 'lit';
import {customElement} from 'lit/decorators.js';
import {classMap} from 'lit/directives/class-map.js';
import './my-menu.js'
@customElement('my-select')
export class Select extends SelectBase {
static styles = styles;
override renderMenu() {
const classes = this.getMenuClasses();
// swap out mwc-menu for my-menu. Bindings and template taken from source.
return html`
<my-menu
innerRole="listbox"
wrapFocus
class=" ${classMap(classes)}"
activatable
.fullwidth=${this.fixedMenuPosition ? false : !this.naturalMenuWidth}
.open=${this.menuOpen}
.anchor=${this.anchorElement}
@selected=${this.onSelected}
@opened=${this.onOpened}
@closed=${this.onClosed}
@items-updated=${this.onItemsUpdated}
@keydown=${this.handleTypeahead}>
${this.renderMenuContent()}
</my-menu>`;
}
}
declare global {
interface HTMLElementTagNameMap {
'my-select': Select;
}
}
{
"dependencies": {
"lit": "^2.0.0",
"@lit/reactive-element": "^1.0.0",
"lit-element": "^3.0.0",
"lit-html": "^2.0.0"
}
}
import {html, css, LitElement} from 'lit';
import {customElement, property, query} from 'lit/decorators.js';
import '@material/mwc-button';
import '@material/mwc-dialog';
import type {Dialog} from '@material/mwc-dialog';
import '@material/mwc-list/mwc-list-item.js';
import '@material/mwc-select';
import './my-select.js';
@customElement('simple-greeting')
export class SimpleGreeting extends LitElement {
static styles = css`
.noOverflow {
border: 1px solid black;
padding: 8px;
overflow: hidden;
/* This changes what children postition:fixed elements are relative to */
transform: translate(0px);
}
table {
/* This changes what children postition:fixed elements are relative to */
transform: translate(0px);
}`;
@query('mwc-dialog') dialogEl!: Dialog;
render() {
return html`
<table>
<tr>
<th>Old (default)</th>
<th>Old (fixedMenuPosition)</th>
<th>New (with native dialog)</th>
</tr>
<tr>
<td class="noOverflow">
<mwc-select outlined>
<mwc-list-item selected></mwc-list-item>
<mwc-list-item value="apple">Apple</mwc-list-item>
<mwc-list-item value="banana">Banana</mwc-list-item>
<mwc-list-item value="coconut">Coconut</mwc-list-item>
<mwc-list-item value="durian">Durian</mwc-list-item>
</mwc-select>
</td>
<td class="noOverflow">
<mwc-select outlined fixedMenuPosition>
<mwc-list-item selected></mwc-list-item>
<mwc-list-item value="apple">Apple</mwc-list-item>
<mwc-list-item value="banana">Banana</mwc-list-item>
<mwc-list-item value="coconut">Coconut</mwc-list-item>
<mwc-list-item value="durian">Durian</mwc-list-item>
</mwc-select>
</td>
<td class="noOverflow">
<my-select outlined>
<mwc-list-item selected></mwc-list-item>
<mwc-list-item value="apple">Apple</mwc-list-item>
<mwc-list-item value="banana">Banana</mwc-list-item>
<mwc-list-item value="coconut">Coconut</mwc-list-item>
<mwc-list-item value="durian">Durian</mwc-list-item>
</my-select>
</td>
</tr>
</table>
<mwc-button raised @click=${() => this.dialogEl.show()}>Click To Open Dialog</mwc-button>
<mwc-dialog>
<table>
<tr>
<th>Old (default)</th>
<th>Old (fixedMenuPosition)</th>
<th>New (with native dialog)</th>
</tr>
<tr>
<td>
<mwc-select outlined>
<mwc-list-item selected></mwc-list-item>
<mwc-list-item value="apple">Apple</mwc-list-item>
<mwc-list-item value="banana">Banana</mwc-list-item>
<mwc-list-item value="coconut">Coconut</mwc-list-item>
<mwc-list-item value="durian">Durian</mwc-list-item>
</mwc-select>
</td>
<td>
<mwc-select outlined fixedMenuPosition>
<mwc-list-item selected></mwc-list-item>
<mwc-list-item value="apple">Apple</mwc-list-item>
<mwc-list-item value="banana">Banana</mwc-list-item>
<mwc-list-item value="coconut">Coconut</mwc-list-item>
<mwc-list-item value="durian">Durian</mwc-list-item>
</mwc-select>
</td>
<td>
<my-select outlined>
<mwc-list-item selected></mwc-list-item>
<mwc-list-item value="apple">Apple</mwc-list-item>
<mwc-list-item value="banana">Banana</mwc-list-item>
<mwc-list-item value="coconut">Coconut</mwc-list-item>
<mwc-list-item value="durian">Durian</mwc-list-item>
</my-select>
</td>
</tr>
</table>
<mwc-dialog>
`;
}
}
{
"files": {
"simple-greeting.ts": {
"position": 0
},
"index.html": {
"position": 1
},
"package.json": {
"position": 2,
"hidden": true
},
"my-select.ts": {
"position": 3
},
"my-menu.ts": {
"position": 4
},
"my-menu-surface.ts": {
"position": 5
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment