Project import generated by Copybara.
GitOrigin-RevId: 63746295f1a5ab5a619056791995793d65529e62
diff --git a/node_modules/mdl-ext/src/menu-button/_menu-button.scss b/node_modules/mdl-ext/src/menu-button/_menu-button.scss
new file mode 100644
index 0000000..9fd84d4
--- /dev/null
+++ b/node_modules/mdl-ext/src/menu-button/_menu-button.scss
@@ -0,0 +1,181 @@
+@charset "UTF-8";
+
+/**
+ * @license
+ * Copyright 2016 Leif Olsen. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This code is built with Google Material Design Lite,
+ * which is Licensed under the Apache License, Version 2.0
+ */
+
+/* Moved to aria-expanded-toggle.scss
+
+.mdlext-aria-expanded-plus-minus {
+ @include mdlext-aria-expanded-toggle($font-family: inherit);
+}
+
+.mdlext-aria-expanded-more-less {
+ @include mdlext-aria-expanded-toggle($icon: 'expand_more', $icon-expanded: 'expand_less');
+}
+*/
+
+.mdlext-menu-button {
+ box-sizing: border-box;
+ @include typo-menu();
+ text-transform: none;
+ position: relative;
+ height: $button-height;
+ padding: 0 $button-padding;
+ display: flex;
+ align-items: center;
+ align-self: stretch;
+
+ > * {
+ margin: 0;
+ padding: 0 0 0 8px;
+ }
+
+ > *:first-child {
+ padding-left: 0;
+ }
+
+ > *:last-child:not(:only-child):not(.mdlext-menu__item__caption) {
+ margin-left: auto; // If more than one element, push last element to the right
+ }
+
+}
+
+.mdlext-menu-button__caption {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ line-height: 1.2;
+}
+
+.mdlext-menu,
+.mdlext-menu__item {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ display: flex;
+}
+
+.mdlext-menu {
+ position: absolute; //fixed;
+ background: $default-dropdown-bg-color;
+ z-index: 1000;
+ min-width: 124px;
+ border-radius: 2px;
+ @include shadow-3dp();
+ display: inline-flex;
+ flex-direction: column;
+ padding: 0;
+ overflow: hidden;
+ overflow-y: auto;
+
+ &:focus {
+ outline-offset: -1px;
+ outline-width: 1px;
+ }
+
+ &[hidden] {
+ @include mdlext-visually-hidden;
+ pointer-events: none;
+ }
+
+ &__item {
+ @include typo-body-1();
+ color: $default-item-text-color;
+ background-color: $default-dropdown-bg-color;
+ position: relative;
+ padding: 0 16px 0 24px;
+ align-items: center;
+ align-self: stretch;
+ text-decoration: none;
+ cursor: pointer;
+ white-space: nowrap;
+ user-select: none;
+ min-height: 40px;
+ overflow: hidden;
+
+ &[aria-selected='true'] {
+ background-color: $default-item-active-bg-color;
+ }
+
+ // checkmark
+ &[aria-selected='true']::before {
+ content:'\2713';
+ position: absolute;
+ font-size: 1.4em;
+ left: 4px;
+ top: 50%;
+ transform: translateY(-50%);
+ pointer-events: none;
+ }
+
+ &:hover:not([disabled]) {
+ background-color: $default-item-hover-bg-color;
+ }
+
+ &:focus {
+ outline-offset: -2px;
+ outline-width: 1px;
+ outline-color: $default-item-outline-color;
+ background-color: $default-item-focus-bg-color;
+ }
+
+ &::-moz-focus-inner {
+ border: 0;
+ }
+
+ &[disabled] {
+ color: $disabled-item-text-color;
+ background-color: transparent;
+ cursor: auto;
+ pointer-events: none;
+
+ > * {
+ color: $disabled-item-text-color;
+ }
+ }
+
+ &__caption {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ > * {
+ margin: 0;
+ padding: 0 0 0 8px;
+ }
+
+ > *:first-child {
+ padding-left: 0;
+ }
+
+ > *:last-child:not(:only-child):not(.mdlext-menu__item__caption) {
+ margin-left: auto; // If more than one element, push last element to the right
+ }
+
+ }
+ &__item-separator {
+ margin: 0;
+ padding: 0;
+ border-bottom: 1px solid $default-item-divider-color;
+ }
+
+}
diff --git a/node_modules/mdl-ext/src/menu-button/menu-button.js b/node_modules/mdl-ext/src/menu-button/menu-button.js
new file mode 100644
index 0000000..e7e37d8
--- /dev/null
+++ b/node_modules/mdl-ext/src/menu-button/menu-button.js
@@ -0,0 +1,758 @@
+/**
+ * @license
+ * Copyright 2016 Leif Olsen. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This code is built with Google Material Design Lite,
+ * which is Licensed under the Apache License, Version 2.0
+ */
+
+
+/**
+ * A menu button is a button that opens a menu. It is often styled as a
+ * typical push button with a downward pointing arrow or triangle to hint
+ * that activating the button will display a menu.
+ */
+import { randomString } from '../utils/string-utils';
+import fullThrottle from '../utils/full-throttle';
+import {
+ VK_TAB,
+ VK_ENTER,
+ VK_ESC,
+ VK_SPACE,
+ VK_END,
+ VK_HOME,
+ VK_ARROW_LEFT,
+ VK_ARROW_UP,
+ VK_ARROW_RIGHT,
+ VK_ARROW_DOWN,
+ IS_UPGRADED,
+} from '../utils/constants';
+
+import { getScrollParents, tether } from '../utils/dom-utils';
+
+const JS_MENU_BUTTON = 'mdlext-js-menu-button';
+const MENU_BUTTON_MENU = 'mdlext-menu';
+const MENU_BUTTON_MENU_ITEM = 'mdlext-menu__item';
+const MENU_BUTTON_MENU_ITEM_SEPARATOR = 'mdlext-menu__item-separator';
+//const MDL_LAYOUT_CONTENT = 'mdl-layout__content';
+
+/**
+ * Creates the menu controlled by the menu button
+ * @param element
+ * @return {{element: Element, selected: Element, open: (function(*=)), removeListeners: (function()), downgrade: (function())}}
+ */
+
+const menuFactory = element => {
+
+ let ariaControls = null;
+ let parentNode = null;
+
+ const removeAllSelected = () => {
+ [...element.querySelectorAll(`.${MENU_BUTTON_MENU_ITEM}[aria-selected="true"]`)]
+ .forEach(selectedItem => selectedItem.removeAttribute('aria-selected'));
+ };
+
+ const setSelected = (item, force=false) => {
+ if(force || (item && !item.hasAttribute('aria-selected'))) {
+ removeAllSelected();
+ if(item) {
+ item.setAttribute('aria-selected', 'true');
+ }
+ }
+ };
+
+ const getSelected = () => {
+ return element.querySelector(`.${MENU_BUTTON_MENU_ITEM}[aria-selected="true"]`);
+ };
+
+ const isDisabled = item => item && item.hasAttribute('disabled');
+
+ const isSeparator = item => item && item.classList.contains(MENU_BUTTON_MENU_ITEM_SEPARATOR);
+
+ const focus = item => {
+ if(item) {
+ item = item.closest(`.${MENU_BUTTON_MENU_ITEM}`);
+ }
+ if(item) {
+ item.focus();
+ }
+ };
+
+ const nextItem = current => {
+ let n = current.nextElementSibling;
+ if(!n) {
+ n = element.firstElementChild;
+ }
+ if(!isDisabled(n) && !isSeparator(n)) {
+ focus(n);
+ }
+ else {
+ let i = element.children.length;
+ while(n && i-- > 0) {
+ if(isDisabled(n) || isSeparator(n)) {
+ n = n.nextElementSibling;
+ if(!n) {
+ n = element.firstElementChild;
+ }
+ }
+ else {
+ focus(n);
+ break;
+ }
+ }
+ }
+ };
+
+ const previousItem = current => {
+ let p = current.previousElementSibling;
+ if(!p) {
+ p = element.lastElementChild;
+ }
+ if(!isDisabled(p) && !isSeparator(p)) {
+ focus(p);
+ }
+ else {
+ let i = element.children.length;
+ while(p && i-- > 0) {
+ if(isDisabled(p) || isSeparator(p)) {
+ p = p.previousElementSibling;
+ if(!p) {
+ p = element.lastElementChild;
+ }
+ }
+ else {
+ focus(p);
+ break;
+ }
+ }
+ }
+ };
+
+ const firstItem = () => {
+ const item = element.firstElementChild;
+ if(isDisabled(item) || isSeparator(item) ) {
+ nextItem(item);
+ }
+ else {
+ focus(item);
+ }
+ };
+
+ const lastItem = () => {
+ const item = element.lastElementChild;
+ if(isDisabled(item) || isSeparator(item)) {
+ previousItem(item);
+ }
+ else {
+ focus(item);
+ }
+ };
+
+ const selectItem = item => {
+ if(item && !isDisabled(item) && !isSeparator(item)) {
+ setSelected(item);
+ close(true, item);
+ }
+ };
+
+ const keyDownHandler = event => {
+
+ const item = event.target.closest(`.${MENU_BUTTON_MENU_ITEM}`);
+
+ switch (event.keyCode) {
+ case VK_ARROW_UP:
+ case VK_ARROW_LEFT:
+ if(item) {
+ previousItem(item);
+ }
+ else {
+ firstItem();
+ }
+ break;
+
+ case VK_ARROW_DOWN:
+ case VK_ARROW_RIGHT:
+ if(item) {
+ nextItem(item);
+ }
+ else {
+ lastItem();
+ }
+ break;
+
+ case VK_HOME:
+ firstItem();
+ break;
+
+ case VK_END:
+ lastItem();
+ break;
+
+ case VK_SPACE:
+ case VK_ENTER:
+ selectItem(item);
+ break;
+
+ case VK_ESC:
+ close(true);
+ break;
+
+ case VK_TAB:
+ // We do not have a "natural" tab order from menu, so the best we can do is to set focus back to the button
+ close(true);
+ break;
+
+ default:
+ return;
+ }
+ event.preventDefault();
+ };
+
+
+ const blurHandler = event => {
+
+ // See: https://github.com/facebook/react/issues/2011
+ const t = event.relatedTarget ||
+ event.explicitOriginalTarget || // FF
+ document.activeElement; // IE11
+
+ //console.log('***** blur, target, relatedTarget', event.target, t);
+
+ try {
+ if (t) {
+ if (t.closest(`.${MENU_BUTTON_MENU}`) !== element && shouldClose(t)) {
+ close();
+ }
+ }
+ else {
+ close();
+ }
+ }
+ catch(err) {
+ // FF throws error: "TypeError: n.closest is not a function" if related target is a text node
+ close();
+ }
+ };
+
+ const clickHandler = event => {
+ //console.log('***** click, target', event.target);
+
+ event.preventDefault();
+ const t = event.target;
+ if (t && t.closest(`.${MENU_BUTTON_MENU}`) === element) {
+ const item = t.closest(`.${MENU_BUTTON_MENU_ITEM}`);
+ if (item) {
+ selectItem(item);
+ }
+ }
+ else {
+ if (shouldClose(t)) {
+ close();
+ }
+ }
+ };
+
+ const touchStartHandler = event => {
+ //console.log('***** touchStart, target', event.target);
+
+ const t = event.target;
+ if(!(t && t.closest(`.${MENU_BUTTON_MENU}`) === element)) {
+ if (event.type === 'touchstart') {
+ event.preventDefault();
+ }
+ close();
+ }
+ };
+
+ const addListeners = () => {
+ element.addEventListener('keydown', keyDownHandler, false);
+ element.addEventListener('blur', blurHandler, true);
+ element.addEventListener('click', clickHandler, true);
+ document.documentElement.addEventListener('touchstart', touchStartHandler, true);
+ };
+
+ const removeListeners = () => {
+ element.removeEventListener('keydown', keyDownHandler, false);
+ element.removeEventListener('blur', blurHandler, true);
+ element.removeEventListener('click', clickHandler, true);
+ document.documentElement.removeEventListener('touchstart', touchStartHandler, true);
+ };
+
+ const open = (controlElement, position='first') => {
+
+ ariaControls = controlElement.closest(`.${JS_MENU_BUTTON}`);
+
+ element.style['min-width'] = `${Math.max(124, controlElement.getBoundingClientRect().width)}px`;
+ element.removeAttribute('hidden');
+ tether(controlElement, element);
+
+ let item;
+ switch (position.toLowerCase()) {
+ case 'first':
+ firstItem();
+ break;
+
+ case 'last':
+ lastItem();
+ break;
+
+ case 'selected':
+ item = getSelected();
+ if(item && !item.hasAttribute('disabled')) {
+ focus(item);
+ }
+ else {
+ firstItem();
+ }
+ break;
+ }
+
+ addListeners();
+ };
+
+
+ const shouldClose = target => {
+ //console.log('***** shouldClose');
+
+ let result = false;
+ const btn = (target && target.closest(`.${JS_MENU_BUTTON}`)) || null;
+ if(!btn) {
+ result = true;
+ }
+ else if(btn.getAttribute('aria-controls') === element.id) {
+ if(btn !== ariaControls) {
+ result = true;
+ }
+ }
+ else {
+ result = true;
+ }
+ return result;
+ };
+
+ const close = (forceFocus = false, item = null) => {
+ removeListeners();
+
+ element.dispatchEvent(
+ new CustomEvent('_closemenu', {
+ bubbles: true,
+ cancelable: true,
+ detail: { forceFocus: forceFocus, item: item }
+ })
+ );
+ };
+
+ const addWaiAria = () => {
+ if (!element.hasAttribute('id')) {
+ // Generate a random id
+ element.id = `menu-button-${randomString()}`;
+ }
+ element.setAttribute('tabindex', '-1');
+ element.setAttribute('role', 'menu');
+ element.setAttribute('hidden', '');
+
+ [...element.querySelectorAll(`.${MENU_BUTTON_MENU_ITEM}`)].forEach( menuitem => {
+ menuitem.setAttribute('tabindex', '-1');
+ menuitem.setAttribute('role', 'menuitem');
+ });
+
+ [...element.querySelectorAll(`.${MENU_BUTTON_MENU_ITEM_SEPARATOR}`)].forEach( menuitem => {
+ menuitem.setAttribute('role', 'separator');
+ });
+ };
+
+ const init = () => {
+ addWaiAria();
+ parentNode = element.parentNode;
+ element.classList.add('is-upgraded');
+ };
+
+ const downgrade = () => {
+ removeListeners();
+ if(element.parentNode !== parentNode) {
+ parentNode.appendChild(element);
+ }
+ element.classList.remove('is-upgraded');
+ };
+
+ init();
+
+ return {
+ /**
+ * Get the menu element
+ * @returns {Element} the menu element
+ */
+ get element() {
+ return element;
+ },
+
+ /**
+ * Set selected menu item
+ * @param item
+ */
+ set selected(item) {
+ setSelected(item, true);
+ },
+
+ /**
+ * Open menu
+ * @param {Element} controlElement the element where the menu should be aligned to
+ * @param {String} position menuElement item to receive focus after menu element is opened
+ */
+ open: (controlElement, position='first') => open(controlElement, position),
+
+ /**
+ * Remove event listeners.
+ */
+ removeListeners: () => removeListeners(),
+
+ /**
+ * Downgrade menu
+ */
+ downgrade: () => downgrade(),
+ };
+};
+
+
+/**
+ * The menubutton component
+ */
+
+class MenuButton {
+
+ constructor(element) {
+ this.element = element;
+ this.focusElement = undefined;
+ this.focusElementLastScrollPosition = undefined;
+ this.scrollElements = [];
+ this.menu = undefined;
+ this.selectedItem = null;
+ this.init();
+ }
+
+ keyDownHandler = event => {
+ if(!this.isDisabled()) {
+ switch (event.keyCode) {
+ case VK_ARROW_UP:
+ this.openMenu('last');
+ break;
+
+ case VK_ARROW_DOWN:
+ this.openMenu();
+ break;
+
+ case VK_SPACE:
+ case VK_ENTER:
+ this.openMenu('selected');
+ break;
+
+ case VK_ESC:
+ this.closeMenu();
+ break;
+
+ case VK_TAB:
+ this.closeMenu();
+ return;
+
+ default:
+ return;
+ }
+ }
+ //event.stopPropagation();
+ event.preventDefault();
+ };
+
+ clickHandler = () => {
+ if(!this.isDisabled()) {
+ if(this.element.getAttribute('aria-expanded').toLowerCase() === 'true') {
+ this.closeMenu(true);
+ }
+ else {
+ this.openMenu('selected');
+ }
+ }
+ };
+
+ /**
+ * Re-position menu if content is scrolled, window is resized or orientation change
+ * @see https://javascriptweblog.wordpress.com/2015/11/02/of-classes-and-arrow-functions-a-cautionary-tale/
+ */
+ recalcMenuPosition = fullThrottle( () => {
+ const c = this.focusElement.getBoundingClientRect();
+ const dx = this.focusElementLastScrollPosition.left - c.left;
+ const dy = this.focusElementLastScrollPosition.top - c.top;
+ const left = (parseFloat(this.menu.element.style.left) || 0) - dx;
+ const top = (parseFloat(this.menu.element.style.top) || 0) - dy;
+
+ this.menu.element.style.left = `${left}px`;
+ this.menu.element.style.top = `${top}px`;
+ this.focusElementLastScrollPosition = c;
+ });
+
+
+ positionChangeHandler = () => {
+ this.recalcMenuPosition(this);
+ };
+
+ closeMenuHandler = event => {
+ if(event && event.detail) {
+ if(event.detail.item && event.detail.item !== this.selectedItem) {
+ this.selectedItem = event.detail.item;
+ this.dispatchMenuSelect();
+ }
+ this.closeMenu(event.detail.forceFocus);
+ }
+ };
+
+ dispatchMenuSelect() {
+ this.element.dispatchEvent(
+ new CustomEvent('menuselect', {
+ bubbles: true,
+ cancelable: true,
+ detail: { source: this.selectedItem }
+ })
+ );
+ }
+
+ isDisabled() {
+ return this.element.hasAttribute('disabled');
+ }
+
+ removeListeners() {
+ this.element.removeEventListener('keydown', this.keyDownHandler);
+ this.element.removeEventListener('click', this.clickHandler);
+ }
+
+ openMenu(position='first') {
+
+ if(!this.isDisabled() && this.menu) {
+
+ // Close the menu if button position change
+ this.scrollElements = getScrollParents(this.element);
+ this.scrollElements.forEach(el => el.addEventListener('scroll', this.positionChangeHandler));
+
+ window.addEventListener('resize', this.positionChangeHandler);
+ window.addEventListener('orientationchange', this.positionChangeHandler);
+ this.menu.element.addEventListener('_closemenu', this.closeMenuHandler);
+
+ this.menu.selected = this.selectedItem;
+ this.menu.open(this.focusElement, position);
+ this.element.setAttribute('aria-expanded', 'true');
+
+ this.focusElementLastScrollPosition = this.focusElement.getBoundingClientRect();
+ }
+ }
+
+ closeMenu(forceFocus = false) {
+ if(this.menu) {
+ this.menu.removeListeners();
+ this.scrollElements.forEach(el => el.removeEventListener('scroll', this.positionChangeHandler));
+ window.removeEventListener('resize', this.positionChangeHandler);
+ window.removeEventListener('orientationchange', this.positionChangeHandler);
+ this.menu.element.removeEventListener('_closemenu', this.closeMenuHandler);
+
+ if (forceFocus) {
+ this.focus();
+ }
+ this.element.setAttribute('aria-expanded', 'false');
+ this.menu.element.setAttribute('hidden', '');
+ }
+ }
+
+ focus() {
+ if(!this.isDisabled()) {
+ this.focusElement.focus();
+ }
+ }
+
+ init() {
+ const addListeners = () => {
+ this.element.addEventListener('keydown', this.keyDownHandler);
+ this.element.addEventListener('click', this.clickHandler);
+ };
+
+ const addWaiAria = () => {
+ this.element.setAttribute('role', 'button');
+ this.element.setAttribute('aria-expanded', 'false');
+ this.element.setAttribute('aria-haspopup', 'true');
+ };
+
+ const addFocusElement = () => {
+ this.focusElement = this.element.querySelector('input[type="text"]');
+ if(!this.focusElement) {
+ this.focusElement = this.element;
+
+ if(!(this.focusElement.tagName.toLowerCase() === 'button' || this.focusElement.tagName.toLowerCase() === 'input')) {
+ if (!this.focusElement.hasAttribute('tabindex')) {
+ this.focusElement.setAttribute('tabindex', '0');
+ }
+ }
+ }
+ };
+
+ const moveElementToDocumentBody = (element) => {
+ // To position an element on top of all other z-indexed elements, the element should be moved to document.body
+ // See: https://philipwalton.com/articles/what-no-one-told-you-about-z-index/
+
+ if(element.parentNode !== document.body) {
+ return document.body.appendChild(element);
+ }
+ return element;
+ };
+
+ const findMenuElement = () => {
+ let menuElement;
+ const menuElementId = this.element.getAttribute('aria-controls');
+ if(menuElementId !== null) {
+ menuElement = document.querySelector(`#${menuElementId }`);
+ }
+ else {
+ menuElement = this.element.parentNode.querySelector(`.${MENU_BUTTON_MENU}`);
+ }
+ return menuElement;
+ };
+
+ const addMenu = () => {
+ const menuElement = findMenuElement();
+ if(menuElement) {
+ if(menuElement.componentInstance) {
+ this.menu = menuElement.componentInstance;
+ }
+ else {
+ this.menu = menuFactory(menuElement);
+ menuElement.componentInstance = this.menu;
+ moveElementToDocumentBody(menuElement);
+ }
+ this.element.setAttribute('aria-controls', this.menu.element.id);
+ }
+ };
+
+ addFocusElement();
+ addWaiAria();
+ addMenu();
+ this.removeListeners();
+ addListeners();
+ }
+
+ downgrade() {
+ if(this.menu) {
+ // Do not downgrade menu if there are other buttons sharing this menu
+ const related = [...document.querySelectorAll(`.${JS_MENU_BUTTON}[aria-controls="${this.element.getAttribute('aria-controls')}"]`)];
+ if(related.filter( c => c !== this.element && c.getAttribute('data-upgraded').indexOf('MaterialExtMenuButton') >= 0).length === 0) {
+ this.menu.downgrade();
+ }
+ }
+ this.removeListeners();
+ }
+
+}
+
+(function() {
+ 'use strict';
+
+ /**
+ * https://github.com/google/material-design-lite/issues/4205
+ * @constructor
+ * @param {Element} element The element that will be upgraded.
+ */
+ const MaterialExtMenuButton = function MaterialExtMenuButton(element) {
+ this.element_ = element;
+ this.menuButton_ = null;
+
+ // Initialize instance.
+ this.init();
+ };
+ window['MaterialExtMenuButton'] = MaterialExtMenuButton;
+
+
+ // Public methods.
+
+ /**
+ * Get the menu element controlled by this button, null if no menu is controlled by this button
+ * @public
+ */
+ MaterialExtMenuButton.prototype.getMenuElement = function() {
+ return this.menuButton_.menu ? this.menuButton_.menu.element : null;
+ };
+ MaterialExtMenuButton.prototype['getMenuElement'] = MaterialExtMenuButton.prototype.getMenuElement;
+
+ /**
+ * Open menu
+ * @public
+ * @param {String} position one of "first", "last" or "selected"
+ */
+ MaterialExtMenuButton.prototype.openMenu = function(position) {
+ this.menuButton_.openMenu(position);
+ };
+ MaterialExtMenuButton.prototype['openMenu'] = MaterialExtMenuButton.prototype.openMenu;
+
+ /**
+ * Close menu
+ * @public
+ */
+ MaterialExtMenuButton.prototype.closeMenu = function() {
+ this.menuButton_.closeMenu(true);
+ };
+ MaterialExtMenuButton.prototype['closeMenu'] = MaterialExtMenuButton.prototype.closeMenu;
+
+ /**
+ * Get selected menu item
+ * @public
+ * @returns {Element} The selected menu item or null if no item selected
+ */
+ MaterialExtMenuButton.prototype.getSelectedMenuItem = function() {
+ return this.menuButton_.selectedItem;
+ };
+ MaterialExtMenuButton.prototype['getSelectedMenuItem'] = MaterialExtMenuButton.prototype.getSelectedMenuItem;
+
+
+ /**
+ * Set (default) selected menu item
+ * @param {Element} item
+ */
+ MaterialExtMenuButton.prototype.setSelectedMenuItem = function(item) {
+ this.menuButton_.selectedItem = item;
+ };
+ MaterialExtMenuButton.prototype['setSelectedMenuItem'] = MaterialExtMenuButton.prototype.setSelectedMenuItem;
+
+ /**
+ * Initialize component
+ */
+ MaterialExtMenuButton.prototype.init = function() {
+ if (this.element_) {
+ this.menuButton_ = new MenuButton(this.element_);
+ this.element_.addEventListener('mdl-componentdowngraded', this.mdlDowngrade_.bind(this));
+ this.element_.classList.add(IS_UPGRADED);
+ }
+ };
+
+ /**
+ * Downgrade component
+ * E.g remove listeners and clean up resources
+ */
+ MaterialExtMenuButton.prototype.mdlDowngrade_ = function() {
+ this.menuButton_.downgrade();
+ };
+
+ // The component registers itself. It can assume componentHandler is available
+ // in the global scope.
+ /* eslint no-undef: 0 */
+ componentHandler.register({
+ constructor: MaterialExtMenuButton,
+ classAsString: 'MaterialExtMenuButton',
+ cssClass: JS_MENU_BUTTON,
+ widget: true
+ });
+})();
diff --git a/node_modules/mdl-ext/src/menu-button/readme.md b/node_modules/mdl-ext/src/menu-button/readme.md
new file mode 100644
index 0000000..ab3a219
--- /dev/null
+++ b/node_modules/mdl-ext/src/menu-button/readme.md
@@ -0,0 +1,472 @@
+# Menu Button
+![Menu button](../../etc/menu-button.png) ![Menu button](../../etc/menu-button-2.png)
+
+A WAI-ARIA friendly menu button component/widget with roles, attributes and behavior in accordance with the specification
+given in WAI-ARIA Authoring Practices, [section 2.20](https://www.w3.org/TR/wai-aria-practices/#button).
+
+## Introduction
+A menu button is a button that opens a menu. It is often styled as a typical push button with a
+downward pointing arrow or triangle to hint that activating the button will display a menu.
+
+A menu is a widget that offers a list of choices to the user, such as a set of actions or functions. A menu is (usually)
+opened, or made visible, by activating a menu button. When a user activates a choice in a menu, the menu (usually) closes.
+
+In this release a `<button>`, an `<input type="text">` or a `<div>` (mdl-textfield) can have `role="button"`, and act as
+the control for a menu.
+
+## To include a MDLEXT **menu button** component:
+
+ 1. Code a `<button>` element; this is the clickable toggle that will show and hide the menu. Include an `aria-controls`
+attribute whose value will match the id attribute of the unordered list coded in the next step. Inside the button,
+code a `<span>` element to hold the button caption text and a `<i>` element to contain a state icon.
+
+```html
+<button aria-controls="menu1">
+ <span></span>
+ <i></i>
+</button>
+```
+
+ 2. Code a `<ul>` unordered list element; this is the container that holds the menu choices. Include an id attribute
+whose value will match the `aria-controls` attribute of the button element.
+
+```html
+<ul id="menu1">
+</ul>
+```
+
+ 3. Inside the unordered list, code one `<li>` element for each choice. Add a text caption as appropriate.
+
+```html
+<ul id="menu1">
+ <li>Small</li>
+ <li>Medium</li>
+ <li>Large</li>
+</ul>
+```
+
+ 4. Add one or more MDL classes, separated by spaces, to the button, span and i elements using the class attribute.
+
+```html
+<button class="mdl-button mdl-js-button" aria-controls="menu1">
+ <span>Select a value</span>
+ <i class="material-icons">more_vert</i>
+</button>
+```
+
+ 5. Add the `mdlext-js-menu-button` class to define the element as a menu button component.
+
+```html
+<button class="mdl-button mdl-js-button mdlext-js-menu-button" aria-controls="menu1">
+ <span>Select a value</span>
+ <i class="material-icons">more_vert</i>
+</button>
+```
+
+ 6. Add the `mdlext-menu` class to the unordered list and the `mdlext-menu__item` class to the list items.
+
+```html
+<ul id="menu1" class="mdlext-menu">
+ <li class="mdlext-menu__item">Small</li>
+ <li class="mdlext-menu__item">Medium</li>
+ <li class="mdlext-menu__item">Large</li>
+</ul>
+```
+
+The menu button component is ready for use.
+
+>**Note:** After page load, the component will add all required Aria roles and attributes.
+
+```html
+<button class="mdl-button mdl-js-button mdlext-js-menu-button"
+ role="button" aria-expanded="false" aria-haspopup="true" aria-controls="menu1">
+ <span>Select a value</span>
+ <i class="material-icons">more_vert</i>
+</button>
+<ul id="menu1" class="mdlext-menu" role="menu" tabindex="-1" hidden>
+ <li class="mdlext-menu__item" role="menuitem" tabindex="-1">Small</li>
+ <li class="mdlext-menu__item" role="menuitem" tabindex="-1">Medium</li>
+ <li class="mdlext-menu__item" role="menuitem" tabindex="-1">Large</li>
+</ul>
+```
+
+
+### Examples
+
+**A menu button with three options.**
+
+```html
+<button class="mdl-button mdl-js-button mdlext-js-menu-button" aria-controls="menu1">
+ <span>Select a value</span>
+ <i class="material-icons">more_vert</i>
+</button>
+<ul id="menu1" class="mdlext-menu">
+ <li class="mdlext-menu__item">Small</li>
+ <li class="mdlext-menu__item">Medium</li>
+ <li class="mdlext-menu__item">Large</li>
+</ul>
+```
+
+> **Note:** If the button and the menu has a common ancestor element, the `aria-controls` attribute of the button and
+the `id` attribute of the menu may be skipped.
+
+```html
+<div role="presentation">
+ <button class="mdl-button mdl-js-button mdlext-js-menu-button">
+ <span>Select a value</span>
+ <i class="material-icons">more_vert</i>
+ </button>
+ <ul class="mdlext-menu">
+ <li class="mdlext-menu__item">Small</li>
+ <li class="mdlext-menu__item">Medium</li>
+ <li class="mdlext-menu__item">Large</li>
+ </ul>
+</div>
+```
+
+With this markup the component will generate a random `id` attribute for the menu and associate it with the `aria-controls`
+of the button.
+
+
+**A menu button with a select listener. Uses a data-value attribute to pass the actual value.**
+
+```html
+<div>
+ <button id="my-button" class="mdl-button mdl-js-button mdl-button--icon mdl-button--primary mdlext-js-menu-button"
+ onmenuselect="document.querySelector('#selection').innerHTML = 'Selected value: ' + event.detail.source.getAttribute('data-value');">
+ <span>Select</span>
+ <i class="mdlext-aria-expanded-more-less"></i>
+ </button>
+ <ul class="mdlext-menu" hidden >
+ <li class="mdlext-menu__item" data-value="10">Ten</li>
+ <li class="mdlext-menu__item" data-value="25">Twentyfive</li>
+ <li class="mdlext-menu__item" data-value="50">Fifty</li>
+ </ul>
+</div>
+```
+```javascript
+document.querySelector('#my-button').addEventListener('menuselect', function(event) {
+ this.querySelector('span').innerHTML = 'Selected value: ' +
+ event.detail.source.getAttribute('data-value')
+});
+```
+
+
+**A menu button decorated with icons**
+
+```html
+<style>
+.material-icons.md-18 {
+ font-size: 18px;
+}
+</style>
+
+<button class="mdl-button mdl-button--raised mdl-js-ripple-effect mdl-js-button mdlext-js-menu-button"
+ aria-controls="demo-menu">
+ <i class="material-icons">gesture</i>
+ <span class="mdlext-menu-button__caption">Select</span>
+ <i class="material-icons mdlext-aria-expanded-more-less"></i>
+</button>
+
+<ul id="demo-menu" class="mdlext-menu" hidden>
+ <li class="mdlext-menu__item">
+ <i class="material-icons md-18">info</i>
+ <span class="mdlext-menu__item__caption">Menu item #1</span>
+ </li>
+ <li class="mdlext-menu__item">
+ <i class="material-icons md-18">help_outline</i>
+ <span class="mdlext-menu__item__caption">Menu item #2. A long text to check ellipsis overflow 0123456789</span>
+ <i class="material-icons md-18">radio</i>
+ </li>
+ <li class="mdlext-menu__item-separator"></li>
+ <li class="mdlext-menu__item" disabled>
+ <span class="mdlext-menu__item__caption">Menu item #3, disabled</span>
+ <i class="material-icons md-18">accessibility</i>
+ </li>
+ <li class="mdlext-menu__item-separator"></li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #IV</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #V</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item-separator"></li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #VI</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #VII</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ Menu item #n
+ </li>
+</ul>
+```
+
+**A mdl-textfield component can be used as a menu button.**
+
+```html
+<style>
+ .mdl-textfield.mdlext-js-menu-button .mdl-textfield__input {
+ padding-right: 40px;
+ }
+ .mdl-textfield__icon {
+ width: 32px;
+ text-align: left;
+ position: absolute;
+ right: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ }
+ .mdl-textfield.is-disabled .mdl-textfield__icon {
+ color: rgba(0, 0, 0, 0.26) !important;
+ }
+ .mdl-textfield.is-invalid .mdl-textfield__icon {
+ color: #de3226 !important;
+ }
+</style>
+
+<div role="presentation">
+ <div id="my-textfield" class="mdl-textfield mdl-js-textfield mdlext-js-menu-button">
+ <input class="mdl-textfield__input" type="text" readonly>
+ <label class="mdl-textfield__label">Sign in with ...</label>
+ <i class="material-icons mdl-textfield__icon mdlext-aria-expanded-more-less"></i>
+ </div>
+ <ul class="mdlext-menu" hidden >
+ <li class="mdlext-menu__item" data-key="S">Small</li>
+ <li class="mdlext-menu__item" data-key="M">Medium</li>
+ <li class="mdlext-menu__item" data-key="L">Large</li>
+ </ul>
+</div>
+```
+```javascript
+document.querySelector('#my-textfield').addEventListener('menuselect', function(event) {
+ this.MaterialTextfield.change(event.detail.source.getAttribute('data-key')
+ + ': ' + event.detail.source.querySelector('span').innerHTML);
+});
+```
+
+**Create your own state icon with SASS.**
+The [_mixins.scss](../_mixins.scss) has a mixin which can be used to create custom state icons.
+
+```sass
+@charset "UTF-8";
+.my-aria-expanded-state {
+ @include mdlext-aria-expanded-toggle($icon: 'arrow_downward', $icon-expanded: 'arrow_upward');
+}
+```
+
+**Use a custom styled `div` as a menu button.**
+
+```html
+<div role="presentation">
+ <div id="my-div" class="mdlext-menu-button mdlext-js-menu-button"
+ style="width:300px; height:44px; max-width:100%; border:1px solid green">
+
+ <span class="mdlext-menu-button__caption">Select a size ...</span>
+ <i class="material-icons my-aria-expanded-state"></i>
+ </div>
+ <ul class="mdlext-menu" hidden >
+ <li class="mdlext-menu__item" data-key="S">Small</li>
+ <li class="mdlext-menu__item" data-key="M">Medium</li>
+ <li class="mdlext-menu__item" data-key="L">Large</li>
+ </ul>
+</div>
+```
+```javascript
+document.querySelector('#my-div').addEventListener('menuselect', function(event) {
+ this.querySelector('span').innerHTML =
+ event.detail.source.getAttribute('data-key') + ': ' +
+ event.detail.source.querySelector('span').innerHTML);
+});
+```
+
+**Many buttons can share one menu.**
+
+```html
+<button class="mdl-button mdl-js-button mdlext-js-menu-button" aria-controls="shared-menu">
+ <span class="mdlext-menu-button__caption">A button</span>
+</button>
+
+<div class="mdl-textfield mdl-js-textfield mdlext-js-menu-button" aria-controls="shared-menu">
+ <input class="mdl-textfield__input" type="text" readonly>
+ <label class="mdl-textfield__label">A MDL textfield</label>
+</div>
+
+<ul id="shared-menu" class="mdlext-menu" hidden>
+ <li class="mdlext-menu__item" role="menuitem">Menu item #1</li>
+ <li class="mdlext-menu__item" role="menuitem">Menu item #2</li>
+ <li class="mdlext-menu__item" role="menuitem">Menu item #n</li>
+</ul>
+```
+
+
+### More examples
+* The [snippets/menu-button.html](./snippets/menu-button.html) and the [tests](../../test/menu-button/menu-button.spec.js) provides more detailed examples.
+* Try out the [live demo](http://leifoolsen.github.io/mdl-ext/demo/menu-button.html)
+
+
+## Characteristics
+
+### Keyboard interaction, Menu Button
+* With focus on the button:
+ * <kbd>Space</kbd> or <kbd>Enter</kbd>: opens the menu, sets `aria-expanded="true"`, and place focus on the previously selected menu item - or on the first menu item if no selected menu item.
+ * <kbd>Down Arrow</kbd>: opens the menu, sets `aria-expanded="true"`, and moves focus to the first menu item.
+ * <kbd>Up Arrow</kbd>: opens the menu, sets `aria-expanded="true"`, and moves focus to the last menu item.
+
+### Mouse interaction, Menu Button
+* With focus on the button:
+ * <kbd>Click</kbd>: opens the menu, sets `aria-expanded="true"`, and place focus on the previously selected menu item - or on the first menu item if no selected menu item.
+ * <kbd>Click</kbd>: a second click closes the menu, sets `aria-expanded="false"` and place focus on button.
+
+### Keyboard interaction, Menu
+* With focus on the menu:
+ * <kbd>Space</kbd> or <kbd>Enter</kbd>: sets `aria-selected="true"` on active menu item, sets `aria-expanded="true"` on menu button, closes menu and moves focus back to menu button. The button emits a custom `select` event with a referfence to the selected menu element.
+ * <kbd>Home</kbd>: move focus to first menu item.
+ * <kbd>End</kbd>: move focus to last menu item.
+ * <kbd>Up Arrow</kbd> or <kbd>Left Arrow</kbd>: move focus to previous menu item.
+ * <kbd>Down Arrow</kbd> or <kbd>Right Arrow</kbd>: Move focus to next menu item.
+ * <kbd>Esc</kbd>: Closes the menu, sets `aria-expanded="true"` on menu button, and move focus back to menu button.
+
+>The keyboard behavior after the menu is open are described in more detail in WAI-ARIA Authoring Practices, [2.19 Menu or Menu bar](https://www.w3.org/TR/wai-aria-practices/#menu).
+
+### Mouse interaction, Menu
+* With focus on the menu:
+ * <kbd>Click</kbd>: sets `aria-selected="true"` on active menu item, sets `aria-expanded="true"` on menu button, closes menu and moves focus back to menu button. The button emits a custom `select` event with a referfence to the selected menu element.
+
+## WAI-ARIA Roles, States, and Properties
+The menu button has the following roles, states and properties set by the menu button component.
+
+### Menu Button
+* `role="button"`: the element that opens the menu has role [button](http://www.w3.org/TR/wai-aria-1.1/#button).
+* `aria-haspopup`: the element with role `button` has [aria-haspopup](http://www.w3.org/TR/wai-aria-1.1/#aria-haspopup) set to `true`.
+* `aria-controls`: identfies the content on the page (e.g. using IDREFs) that this menu button controls.
+* `aria-expanded`: the element with role `button` has [aria-expanded](https://www.w3.org/TR/wai-aria-1.1/#aria-expanded) set to `true` if the corresponding menu is open, oterwise false.
+* `aria-expanded`: when a menu item is disabled, `aria-disabled` is set to `true`.
+* `disabled"`: indicates that a button is disabled, otherwise not present.
+
+### Menu, WAI-ARIA Roles
+* `role="menu"`: identifies the element as a menu widget.
+* `hidden`: the menu has attrubute hidden if the controlling buttoun has `aria-expanded="false"`, otherwise the attribute is not present.
+* `role="menuitem"`: identifies an element as a menu item widget.
+* `role="menuitemcheckbox"`: (not yet implemented).
+* `role="menuitemradion"`: (not yet implemented).
+* `aria-selected`: the selected menu item has `aria-selected="true"`, otherwise not present.
+* `role="separator"`: a divider that separates and distinguishes sections of content or groups of menuitems..
+* `disabled"`: indicates that a menu item is disabled, otherwise not present.
+
+
+>The roles, states, and properties needed for the menu are described in more detail in WAI-ARIA Authoring Practices, [2.19 Menu or Menu bar](https://www.w3.org/TR/wai-aria-practices/#menu).
+
+## Events emitted from the component
+The menu button emits a custom `menuselect` event when a menu item is selected. The event has a detail object with the following structure:
+
+```javascript
+detail: {
+ source: item // The selected menu item
+}
+```
+
+To set up an event listener to receive the select custom event.
+```javascript
+document.querySelector('#my-menubutton').addEventListener('menuselect', function(e) {
+ console.log('menu item selected:', e.detail.source);
+});
+```
+Refer to [snippets/menu-button.html](./snippets/menu-button.html) or the [tests](../../test/menu-button/menu-button.spec.js) for detailed usage.
+
+
+## Public methods
+
+### openMenu(position)
+Open menu at given position. Position is on of `first`, `last` or `selected`. Default value is `first`.
+* `first` focus first menu item
+* `last` focus last menu item
+* `selected` focus previously selected menu item
+
+```javascript
+const menuButton = document.querySelector('#my-menu-button');
+menuButton.MaterialExtMenuButton.openMenu();
+```
+
+### closeMenu()
+Closes an open menu. Moves focus to button.
+
+```javascript
+const menuButton = document.querySelector('#my-menu-button');
+menuButton.MaterialExtMenuButton.closeMenu();
+```
+
+### getMenuElement()
+Get the menu element controlled by this button, null if no menu is controlled by this button.
+
+```javascript
+const menuButton = document.querySelector('#my-menu-button');
+const menu = menuButton.MaterialExtMenuButton.getMenuElement();
+```
+
+### getSelectedMenuItem()
+Get a selected menu item element, null if no menu item element selected.
+
+```javascript
+const menuButton = document.querySelector('#my-menu-button');
+const element = menuButton.MaterialExtMenuButton.getSelectedMenuItem();
+console.log('Selected menu item', element);
+```
+
+### setSelectedMenuItem(item)
+Set a selected menu item element, typically before menu is opened.
+
+```javascript
+const menuButton = document.querySelector('#my-menu-button');
+const menu = menuButton.MaterialExtMenuButton.getMenuElement();
+menuButton.MaterialExtMenuButton.setSelectedMenuItem(menu.children[1]);
+```
+
+Refer to [snippets/menu-button.html](./snippets/menu-button.html) or the [tests](../../test/menu-button/menu-button.spec.js) for detailed usage.
+
+## Configuration options
+
+The MDLEXT CSS classes apply various predefined visual and behavioral enhancements to the menu button.
+The table below lists the available classes and their effects.
+
+| MDLEXT class | Effect | Remarks |
+|----------------------------------|--------|---------|
+|`mdlext-menu-button` | Basic styling for a menu button | Optional on a div element |
+|`mdlext-js-menu-button` | Assigns basic MDLEXT behavior to menu button. Identifies the element as a menu button component | Required on the element that should act as a menu button |
+|`mdlext-menu-button__caption` | Holds the button text | Optional on span element inside menu button element - but required if you want to decorate a button with icons. More than one caption can be used to control various aspects of the button text, e.g. font size. |
+|`material-icons` | Defines span as a material icon | Required on an inline element. Decorates button or menu item with an icon |
+|`mdlext-menu` | Defines an unordered list container as an MDLEXT component | Required on ul element |
+|`mdlext-menu__item` | Defines menu options | Required on list item elements |
+|`mdlext-menu__item__caption` | Holds the menu text | Optional on span element inside list item element - but required if you want to decorate a menu item with icons. More than one caption can be used to control various aspects of the menu text, e.g. font size. |
+|`mdlext-menu__item-separator` | Items in a menu may be divided into groups by placing an element with a role of `separator` between groups. | Optional; goes on unordered list element |
+|`mdlext-aria-expanded-plus-minus` | State icon. Displays '+' or '-' | Optional; goes on button element |
+|`mdlext-aria-expanded-more-less` | State icon. Displays 'more' or 'less' Material Design icons | Optional; goes on button element |
+
+> **Note:**
+> Disabled versions of the menu items are provided, and are invoked with the standard HTML boolean attribute `disabled`
+`<li class="mdlext-menu__item" disabled>A menu item</li>`. This attribute may be added or removed programmatically via scripting.
+
+> If you decorate the menu button with icons, wrap the button text inside a span to separate icons and text
+```html
+<button class="mdl-button mdl-button--raised mdl-js-ripple-effect mdl-js-button mdlext-js-menu-button">
+ <i class="material-icons">gesture</i>
+ <span class="mdlext-menu-button__caption">Select</span>
+ <i class="material-icons mdlext-menu-button__aria-expanded-more-less"></i>
+</button>
+```
+
+> If you decorate a menu item with icons, wrap the menu item text inside a span to separate icons and text
+```html
+<ul id="demo-menu" class="mdlext-menu" hidden>
+ <li class="mdlext-menu__item">
+ <i class="material-icons md-18">help_outline</i>
+ <span class="mdlext-menu__item__caption">Menu item</span>
+ <i class="material-icons">info</i>
+ </li>
+</ul>
+```
diff --git a/node_modules/mdl-ext/src/menu-button/snippets/menu-button.html b/node_modules/mdl-ext/src/menu-button/snippets/menu-button.html
new file mode 100644
index 0000000..784f280
--- /dev/null
+++ b/node_modules/mdl-ext/src/menu-button/snippets/menu-button.html
@@ -0,0 +1,594 @@
+<article>
+<p>A menu button is a button that opens a menu. It is often styled as a typical push button with a
+downward pointing arrow or triangle to hint that activating the button will display a menu.
+The menu button has roles, attributes and behaviour as outlined in
+<a href="https://www.w3.org/TR/wai-aria-practices/#menubutton" target="_blank">WAI-ARIA Authoring Practices</a>.
+</p>
+</article>
+
+<link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
+
+<style>
+ .size-22 {
+ font-size: 22px;
+ width: 22px;
+ text-align: center;
+ vertical-align: middle;
+ }
+
+ .mdl-cell p {
+ margin-bottom: 0;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+
+
+ .mdl-textfield.mdlext-js-menu-button .mdl-textfield__input {
+ padding-right: 40px;
+ }
+
+ .mdl-textfield__icon {
+ width: 32px;
+ text-align: left;
+ position: absolute;
+ right: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ }
+
+ .mdl-textfield.is-disabled .mdl-textfield__icon {
+ color: rgba(0, 0, 0, 0.26) !important;
+ }
+
+ .mdl-textfield.is-invalid .mdl-textfield__icon {
+ color: #de3226 !important;
+ }
+
+ #max-width-menu {
+ max-width: 400px;
+ }
+
+</style>
+
+<script>
+ function signedIn(sn) {
+ return 'Signed in with <i class="size-22 icon ion-social-' + sn + '"></i> ';
+ }
+ function signedInIcon(sn) {
+ return '<i class="size-22 icon ion-social-' + sn + '"></i>';
+ }
+</script>
+
+<div class="mdl-grid mdl-grid--no-spacing">
+ <div class="mdl-cell mdl-cell--4-col">
+ <p><code>mdl-button</code></p>
+ <button id="btn-social-1" style="width:100%; max-width:300px; height:46px"
+ class="mdl-button mdl-button--colored mdl-button--raised mdl-js-ripple-effect mdl-js-button mdlext-js-menu-button"
+ aria-controls="shared-social-menu" >
+ <i class="material-icons">person_outline</i>
+ <span class="mdlext-menu-button__caption">Sign in with ...</span>
+ <i class="material-icons mdlext-aria-expanded-more-less"></i>
+ </button>
+ <script>
+ document.querySelector('#btn-social-1').addEventListener('menuselect', function(event) {
+ this.querySelector('.mdlext-menu-button__caption').innerHTML = signedIn(event.detail.source.getAttribute('data-value'));
+ });
+ </script>
+ </div> <!--cell -->
+
+ <div class="mdl-cell mdl-cell--4-col">
+ <p><code>mdl-textfield</code></p>
+ <div id="btn-social-2"
+ class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label mdlext-js-menu-button"
+ aria-controls="shared-social-menu">
+ <input class="mdl-textfield__input" type="text" readonly>
+ <label class="mdl-textfield__label">Sign in with ...</label>
+ <i class="material-icons mdl-textfield__icon mdlext-aria-expanded-more-less"></i>
+ </div>
+ <script>
+ document.querySelector('#btn-social-2').addEventListener('menuselect', function(event) {
+ this.MaterialTextfield.change(event.detail.source.querySelector('span').innerHTML);
+ });
+ </script>
+ </div> <!--cell -->
+
+ <div class="mdl-cell mdl-cell--4-col mdlext-bordered-fields">
+ <p><code>mdlext-bordered-fields</code></p>
+ <div id="btn-social-3"
+ class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label mdlext-bordered-fields__icon-right mdlext-js-menu-button"
+ aria-controls="shared-social-menu">
+ <input class="mdl-textfield__input" type="text" readonly>
+ <label class="mdl-textfield__label">Sign in with ...</label>
+ <i class="material-icons mdlext-aria-expanded-more-less"></i>
+ </div>
+ <script>
+ document.querySelector('#btn-social-3').addEventListener('menuselect', function(event) {
+ this.MaterialTextfield.change(event.detail.source.querySelector('span').innerHTML);
+ });
+ </script>
+ </div> <!--cell -->
+
+ <div class="mdl-cell mdl-cell--4-col">
+ <p>custom styled <code><div></code></p>
+ <div id="btn-social-4"
+ class="mdlext-menu-button mdlext-js-menu-button" style="width:300px; height:46px; max-width:100%; border:1px solid green"
+ aria-controls="shared-social-menu">
+ <span class="mdlext-menu-button__caption">Sign in with ...</span>
+ <i class="material-icons mdlext-aria-expanded-more-less"></i>
+ </div>
+ <script>
+ document.querySelector('#btn-social-4').addEventListener('menuselect', function(event) {
+ this.querySelector('span').innerHTML = signedIn(event.detail.source.getAttribute('data-value')) + ', ' +
+ event.detail.source.querySelector('span').innerHTML;
+ });
+ </script>
+ </div> <!--cell -->
+
+
+ <div class="mdl-cell mdl-cell--4-col">
+ <p>mdl-button--icon</p>
+
+ <button id="btn-social-5" style="height:46px; width:46px;"
+ class="mdl-button mdl-js-button mdl-button--icon mdlext-js-menu-button"
+ aria-controls="shared-social-menu">
+ <i class="material-icons">more_vert</i>
+ </button>
+ <script>
+ document.querySelector('#btn-social-5').addEventListener('menuselect', function(event) {
+ this.innerHTML = signedInIcon(event.detail.source.getAttribute('data-value'));
+ });
+ </script>
+
+ </div> <!--cell -->
+
+</div> <!-- grid -->
+
+<ul id="shared-social-menu" class="mdlext-menu" hidden >
+ <li class="mdlext-menu__item" data-value="twitter">
+ <span class="mdlext-menu__item__caption">Twitter</span>
+ <i class="icon ion-social-twitter-outline size-22"></i>
+ </li>
+ <li class="mdlext-menu__item" data-value="github">
+ <span class="mdlext-menu__item__caption">GitHub</span>
+ <i class="ion-social-github-outline size-22"></i>
+ </li>
+ <li class="mdlext-menu__item" data-value="googleplus">
+ <span class="mdlext-menu__item__caption">G+</span>
+ <i class="ion-social-googleplus-outline size-22"></i>
+ </li>
+ <li class="mdlext-menu__item" data-value="linkedin">
+ <span class="mdlext-menu__item__caption">LinkedIn</span>
+ <i class="icon ion-social-linkedin-outline size-22"></i>
+ </li>
+ <li class="mdlext-menu__item" data-value="facebook">
+ <span class="mdlext-menu__item__caption">Facebook</span>
+ <i class="icon ion-social-facebook-outline size-22"></i>
+ </li>
+</ul>
+
+<p style="margin-top: 16px;">Menu buttons positioned left, middle and right, using <code>textalign</code>.</p>
+<div role="presentation">
+ <button class="mdl-button mdl-button--raised mdl-js-ripple-effect mdl-js-button mdlext-js-menu-button">
+ <i class="material-icons">gesture</i>
+ <span class="mdlext-menu-button__caption">Select</span>
+ <i class="material-icons mdlext-aria-expanded-more-less"></i>
+ </button>
+ <ul id="max-width-menu" class="mdlext-menu" hidden >
+ <li class="mdlext-menu__item">
+ <i class="material-icons md-18">info</i>
+ <span class="mdlext-menu__item__caption">Menu item #1</span>
+ </li>
+ <li class="mdlext-menu__item">
+ <i class="material-icons md-18">help_outline</i>
+ <span class="mdlext-menu__item__caption">Menu item #2. A long text to check ellipsis overflow 0123456789</span>
+ <i class="material-icons md-18">radio</i>
+ </li>
+ <li class="mdlext-menu__item-separator"></li>
+ <li class="mdlext-menu__item" disabled>
+ <span class="mdlext-menu__item__caption">Menu item #3, disabled</span>
+ <i class="material-icons md-18">accessibility</i>
+ </li>
+ <li class="mdlext-menu__item-separator"></li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #IV</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #V</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item-separator"></li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #VI</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #VII</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ Menu item #n
+ </li>
+ </ul>
+</div>
+
+
+<div role="presentation" style="text-align: center">
+ <button class="mdl-button mdl-button--raised mdl-js-ripple-effect mdl-js-button mdlext-js-menu-button">
+ <i class="material-icons">gesture</i>
+ <span class="mdlext-menu-button__caption">Select</span>
+ <i class="material-icons mdlext-aria-expanded-more-less"></i>
+ </button>
+ <ul class="mdlext-menu" hidden >
+ <li class="mdlext-menu__item">
+ <i class="material-icons md-18">info</i>
+ <span class="mdlext-menu__item__caption">Menu item #1</span>
+ </li>
+ <li class="mdlext-menu__item">
+ <i class="material-icons md-18">help_outline</i>
+ <span class="mdlext-menu__item__caption">Menu item #2</span>
+ <i class="material-icons md-18">radio</i>
+ </li>
+ <li class="mdlext-menu__item" disabled>
+ <span class="mdlext-menu__item__caption">Menu item #3, disabled</span>
+ <i class="material-icons md-18">accessibility</i>
+ </li>
+ <li class="mdlext-menu__item-separator"></li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #IV</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #V</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #VI</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #VII</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #VIII</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #IX</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #X</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #XI</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #XII</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #XIII</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #XIV</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #XV</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #XVI</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #XVII</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #XVIII</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #XIX</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #XX</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ Menu item #n
+ </li>
+ </ul>
+</div>
+
+
+<div role="presentation" style="text-align: right">
+ <button class="mdl-button mdl-button--raised mdl-js-ripple-effect mdl-js-button mdlext-js-menu-button">
+ <i class="material-icons">gesture</i>
+ <span class="mdlext-menu-button__caption">Select</span>
+ <i class="material-icons mdlext-aria-expanded-more-less"></i>
+ </button>
+ <ul class="mdlext-menu" hidden >
+ <li class="mdlext-menu__item">
+ <i class="material-icons md-18">info</i>
+ <span class="mdlext-menu__item__caption">Menu item #1</span>
+ </li>
+ <li class="mdlext-menu__item">
+ <i class="material-icons md-18">help_outline</i>
+ <span class="mdlext-menu__item__caption">Menu item #2</span>
+ <i class="material-icons md-18">radio</i>
+ </li>
+ <li class="mdlext-menu__item" disabled>
+ <span class="mdlext-menu__item__caption">Menu item #3, disabled</span>
+ <i class="material-icons md-18">accessibility</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #IV</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item-separator"></li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #V</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #VI</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #VII</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ Menu item #n
+ </li>
+ </ul>
+</div>
+
+
+<p style="margin-top: 16px">This menu button has WAIA roles and attributes coded in markup</p>
+<button id="aria-demo-button"
+ class="mdl-button mdl-button--raised mdl-button--accent mdl-js-ripple-effect mdl-js-button mdlext-js-menu-button"
+ role="button"
+ aria-haspopup="true"
+ aria-controls="menu-example-dropdown"
+ aria-expanded="false"
+ tabindex="0">
+ <span class="mdlext-menu-button__caption">Select a size</span>
+ <i class="mdlext-aria-expanded-plus-minus"></i>
+</button>
+<script>
+ document.querySelector('#aria-demo-button').addEventListener('menuselect', function(event) {
+ this.querySelector('span').innerHTML = 'Size: ' + event.detail.source.innerHTML;
+ });
+</script>
+<ul id="menu-example-dropdown"
+ class="mdlext-menu"
+ role="menu"
+ hidden >
+ <li class="mdlext-menu__item" role="menuitem">X Small</li>
+ <li class="mdlext-menu__item-separator" role="separator"></li>
+ <li class="mdlext-menu__item" role="menuitem">Small</li>
+ <li class="mdlext-menu__item" role="menuitem">Medium</li>
+ <li class="mdlext-menu__item" role="menuitem">Large</li>
+ <li class="mdlext-menu__item-separator" role="separator"></li>
+ <li class="mdlext-menu__item" role="menuitem">X Large</li>
+ <li class="mdlext-menu__item" role="menuitem">XXX Large</li>
+</ul>
+
+<p style="margin-top: 16px">A disabled menu button should do nothing</p>
+<div role="presentation">
+ <button class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdlext-js-menu-button" disabled>
+ <span class="mdlext-menu-button__caption">Disabled</span>
+ <i class="material-icons mdlext-aria-expanded-more-less"></i>
+ </button>
+ <ul class="mdlext-menu" hidden>
+ <li class="mdlext-menu__item">Menu item #1</li>
+ </ul>
+</div>
+
+
+<p style="margin-top: 16px">Menu buttons inside a table. Menus placed inside and after the table element to verify
+ that positioning works as expected.</p>
+
+<div style="position:relative; border:2px solid green; overflow: hidden;">
+<p>The box with green borders has <code>overflow:hidden</code></p>
+<div>
+
+ <table id="a-table-with-menu-buttons"
+ class="mdl-data-table mdl-js-data-table mdl-data-table--selectable mdl-shadow--2dp" style="width: 48%; float:left;">
+ <thead>
+ <tr>
+ <th class="mdl-data-table__cell--non-numeric">Material</th>
+ <th>Quantity</th>
+ <th>Unit price</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="mdl-data-table__cell--non-numeric">Acrylic (Transparent)</td>
+ <td>
+ <span style="width:40px; display: inline-block;"></span>
+ <button class="mdl-button mdl-js-button mdl-button--icon mdl-button--primary mdlext-js-menu-button"
+ aria-controls="table-menu-example">
+ <i class="mdlext-aria-expanded-more-less"></i>
+ </button>
+ </td>
+ <td>$2.90</td>
+ </tr>
+ <tr>
+ <td class="mdl-data-table__cell--non-numeric">Plywood (Birch)</td>
+ <td>
+ <span style="width:40px; display: inline-block;"></span>
+ <button class="mdl-button mdl-js-button mdl-button--icon mdl-button--primary mdlext-js-menu-button"
+ aria-controls="table-menu-example">
+ <i class="mdlext-aria-expanded-more-less"></i>
+ </button>
+ </td>
+ <td>$1.25</td>
+ </tr>
+ <tr>
+ <td class="mdl-data-table__cell--non-numeric">Laminate (Gold on Blue)</td>
+ <td>
+ <span style="width:40px; display: inline-block;"></span>
+ <button class="mdl-button mdl-button--icon mdl-button--primary mdl-js-button mdlext-js-menu-button"
+ aria-controls="table-menu-example">
+ <i class="mdlext-aria-expanded-more-less"></i>
+ </button>
+ </td>
+ <td>$2.35</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <script>
+ document.querySelector('#a-table-with-menu-buttons').addEventListener('menuselect', function(event) {
+ var span = event.target.closest('td').querySelector('span');
+ span.innerHTML = event.detail.source.getAttribute('data-value');
+ });
+ </script>
+
+ <div role="presentation" style="width:48%; float:right;">
+ <p style="margin-top: 16px">This menu button has a menu with dark color theme, <code>mdlext-dark-color-theme</code> class</p>
+
+ <button class="mdl-button mdl-button--raised mdl-js-ripple-effect mdl-js-button mdlext-js-menu-button">
+ <i class="material-icons">gesture</i>
+ <span class="mdlext-menu-button__caption">Select</span>
+ <i class="material-icons mdlext-aria-expanded-more-less"></i>
+ </button>
+ <ul class="mdlext-menu mdlext-dark-color-theme" hidden >
+ <li class="mdlext-menu__item">
+ <i class="material-icons md-18">info</i>
+ <span class="mdlext-menu__item__caption">Menu item #1</span>
+ </li>
+ <li class="mdlext-menu__item">
+ <i class="material-icons md-18">help_outline</i>
+ <span class="mdlext-menu__item__caption">Menu item #2. A long text to check ellipsis overflow 0123456789</span>
+ <i class="material-icons md-18">radio</i>
+ </li>
+ <li class="mdlext-menu__item-separator"></li>
+ <li class="mdlext-menu__item" disabled>
+ <span class="mdlext-menu__item__caption">Menu item #3, disabled</span>
+ <i class="material-icons md-18">accessibility</i>
+ </li>
+ <li class="mdlext-menu__item-separator"></li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #IV</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #V</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ <li class="mdlext-menu__item-separator"></li>
+ <li class="mdlext-menu__item">
+ <span class="mdlext-menu__item__caption">Menu item #VI</span>
+ <i class="material-icons md-18">build</i>
+ </li>
+ </ul>
+ </div>
+
+</div>
+</div>
+
+<ul id="table-menu-example" class="mdlext-menu" hidden >
+ <li class="mdlext-menu__item" data-value="10">Ten</li>
+ <li class="mdlext-menu__item" data-value="25">Twentyfive</li>
+ <li class="mdlext-menu__item" data-value="50">Fifty</li>
+</ul>
+
+
+<style>
+ .demo-grid {
+ margin-top: 32px;
+ }
+ .demo-grid .mdl-cell {
+ background: rgb(63,81,181);
+ text-align: center;
+ color: white;
+ padding: 16px 8px;
+ border: 1px solid #aaaaaa;
+ }
+ .demo-grid .mdl-cell:nth-child(odd) {
+ background: rgb(33,150,243);
+ }
+</style>
+
+
+<div id="a-grid-with-menu-buttons" class="demo-grid">
+ <p>Menu buttons inside <code>mdl-grid</code></p>
+
+ <div id="menu-button-grid" class="mdl-grid mdl-grid--no-spacing">
+ <div class="mdl-cell mdl-cell--4-col mdl-cell--4-col-tablet mdl-cell--2-col-phone">
+ <button class="mdl-button mdl-button--raised mdl-button--accent mdl-js-ripple-effect mdl-js-button mdlext-js-menu-button">
+ <span class="mdlext-menu-button__caption">Select a size</span>
+ <i class="mdlext-aria-expanded-plus-minus"></i>
+ </button>
+ <ul class="mdlext-menu"
+ role="menu"
+ hidden >
+ <li class="mdlext-menu__item" role="menuitem">Small</li>
+ <li class="mdlext-menu__item" role="menuitem">Medium</li>
+ <li class="mdlext-menu__item" role="menuitem">Large</li>
+ </ul>
+ </div>
+ <div class="mdl-cell mdl-cell--5-col mdl-cell--4-col-tablet mdl-cell--2-col-phone">
+ <button class="mdl-button mdl-button--raised mdl-button--accent mdl-js-ripple-effect mdl-js-button mdlext-js-menu-button">
+ <span class="mdlext-menu-button__caption">Select a size</span>
+ <i class="mdlext-aria-expanded-plus-minus"></i>
+ </button>
+ <ul class="mdlext-menu"
+ role="menu"
+ hidden >
+ <li class="mdlext-menu__item" role="menuitem">Small</li>
+ <li class="mdlext-menu__item" role="menuitem">Medium</li>
+ <li class="mdlext-menu__item" role="menuitem">Large</li>
+ </ul>
+ </div>
+ <div class="mdl-cell mdl-cell--3-col mdl-cell--4-col-tablet mdl-cell--hide-phone">
+ <button class="mdl-button mdl-button--raised mdl-button--accent mdl-js-ripple-effect mdl-js-button mdlext-js-menu-button">
+ <span class="mdlext-menu-button__caption">Select a size</span>
+ <i class="mdlext-aria-expanded-plus-minus"></i>
+ </button>
+ <ul class="mdlext-menu"
+ role="menu"
+ hidden >
+ <li class="mdlext-menu__item" role="menuitem">Small</li>
+ <li class="mdlext-menu__item" role="menuitem">Medium</li>
+ <li class="mdlext-menu__item" role="menuitem">Large</li>
+ </ul>
+ </div>
+ </div>
+</div>
+<script>
+ document.querySelector('#a-grid-with-menu-buttons').addEventListener('menuselect', function(event) {
+ event.target.querySelector('span').innerHTML = 'Size: ' + event.detail.source.innerHTML;
+ });
+</script>
+
+<div style="margin-bottom:256px;"></div>
+
+<!--
+https://www.w3.org/TR/wai-aria-practices/#menubutton
+http://w3c.github.io/aria-practices/examples/menu-button/menu-button-1.html
+http://w3c.github.io/aria-practices/examples/menu-button/menu-button-2.html
+http://tympanus.net/codrops/2012/10/04/custom-drop-down-list-styling/
+http://www.slideshare.net/webaxe/html5-dev-con-2012-aria-widget
+http://terrillthompson.com/blog/474
+https://github.com/davidtheclark/react-aria-menubutton
+https://codepen.io/tiffanytse/pen/Dkvtr
+https://codepen.io/andytran/pen/vLNGvN
+https://codepen.io/dapacreative/pen/WQoLzb
+https://codepen.io/gabrieltomescu/pen/ZGGyPK
+https://codepen.io/Idered/pen/vowrB
+https://codepen.io/Stedesign/pen/DbAJh
+http://markbirbeck.com/2015/10/14/a-mixin-approach-to-material-design-lite-using-sass/
+-->