diff --git a/gulpfile.js b/gulpfile.js index 7378ec22..d8c527b0 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -130,7 +130,7 @@ gulp.task('scripts', function () { 'src/checkbox/checkbox.js', 'src/column-layout/column-layout.js', 'src/icon-toggle/icon-toggle.js', - 'src/item/item.js', + 'src/menu/menu.js', 'src/radio/radio.js', 'src/slider/slider.js', 'src/spinner/spinner.js', diff --git a/src/dropdown-menu/_dropdown-menu.scss b/src/dropdown-menu/_dropdown-menu.scss deleted file mode 100644 index f22414a0..00000000 --- a/src/dropdown-menu/_dropdown-menu.scss +++ /dev/null @@ -1,25 +0,0 @@ -@import "../typography/typography"; -@import "../colors"; -@import "../shadow/shadow"; - -.wsk-dropdown-menu { - list-style : none; - background : $default-dropdown-bg-color; - border : none; - border-radius: 2px; - display : inline-block; - min-width : 124px; - position : relative; - overflow : hidden; - padding : 0; - @include shadow-z1(); - will-change: box-shadow, transform; - transition: box-shadow 0.2s ease-out; -} - -.wsk-dropdown-menu .wsk-item { - display: block; - width: 100%; -} - -/** TODO: DropdownMenu attached to other elements. */ diff --git a/src/dropdown-menu/demo.html b/src/dropdown-menu/demo.html deleted file mode 100644 index eb7be5ba..00000000 --- a/src/dropdown-menu/demo.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - Dropdown Menu - - - - - - - - -
- - - -
- - - - - - - - diff --git a/src/dropdown-menu/demo.scss b/src/dropdown-menu/demo.scss deleted file mode 100644 index 4cd53a4c..00000000 --- a/src/dropdown-menu/demo.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import "../styleguide_demo_bp"; -@import "../item/item"; -@import "_dropdown-menu"; diff --git a/src/index.html b/src/index.html index 14e683b2..ddb0ebc4 100644 --- a/src/index.html +++ b/src/index.html @@ -77,13 +77,8 @@
-

Dropdown Menu

- -
- -
-

Item

- +

Menu

+
diff --git a/src/item/_item.scss b/src/item/_item.scss deleted file mode 100644 index 4c2843f2..00000000 --- a/src/item/_item.scss +++ /dev/null @@ -1,53 +0,0 @@ -@import "../typography/typography"; -@import "../colors"; -@import "../ripple/ripple"; - -.wsk-item { - border : none; - color : $default-item-text-color; - background : transparent; - display : inline-block; - text-align : left; - margin : 0; - padding : 0.6em 1.2em; - outline-color: $default-item-outline-color; - position : relative; - overflow : hidden; - font : inherit; - @include typo-body-1(); - text-decoration: none; - cursor : pointer; -} - -.wsk-item[disabled] { - color : $disabled-item-text-color; - cursor : auto; -} - -.wsk-item:hover { - background-color: $default-item-hover-bg-color; -} - -.wsk-item:focus { - outline: none; - background-color: $default-item-focus-bg-color; -} - -.wsk-item::-moz-focus-inner { - border: 0; -} - -.wsk-item:active { - background-color: $default-item-active-bg-color; -} - -.wsk-item--ripple-container { - display: block; - height: 100%; - left: 0px; - position: absolute; - top: 0px; - width: 100%; - z-index: 0; - overflow: hidden; -} diff --git a/src/item/demo.html b/src/item/demo.html deleted file mode 100644 index f877bcc0..00000000 --- a/src/item/demo.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - Item - - - - - - - - -
- - - - - - -
- - Web Starter Kit - -
- - - - - - - - diff --git a/src/item/demo.scss b/src/item/demo.scss deleted file mode 100644 index 5225fab4..00000000 --- a/src/item/demo.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import "../styleguide_demo_bp"; -@import "_item"; diff --git a/src/item/item.js b/src/item/item.js deleted file mode 100644 index e2f38899..00000000 --- a/src/item/item.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Class constructor for Item WSK component. - * Implements WSK component design pattern defined at: - * https://github.com/jasonmayes/wsk-component-design-pattern - * @param {HTMLElement} element The element that will be upgraded. - */ -function MaterialItem(element) { - 'use strict'; - - this.element_ = element; - - // Initialize instance. - this.init(); -} - -/** - * Store constants in one place so they can be updated easily. - * @enum {string | number} - * @private - */ -MaterialItem.prototype.Constant_ = { - // None for now. -}; - -/** - * Store strings for class names defined by this component that are used in - * JavaScript. This allows us to simply change it in one place should we - * decide to modify at a later date. - * @enum {string} - * @private - */ -MaterialItem.prototype.CssClasses_ = { - WSK_ITEM_RIPPLE_CONTAINER: 'wsk-item--ripple-container', - - WSK_RIPPLE: 'wsk-ripple' -}; - - -/** - * Initialize element. - */ -MaterialItem.prototype.init = function() { - 'use strict'; - - if (this.element_) { - var rippleContainer = document.createElement('span'); - rippleContainer.classList.add(this.CssClasses_.WSK_ITEM_RIPPLE_CONTAINER); - - var ripple = document.createElement('span'); - ripple.classList.add(this.CssClasses_.WSK_RIPPLE); - rippleContainer.appendChild(ripple); - - this.element_.appendChild(rippleContainer); - } -}; - - -// The component registers itself. It can assume componentHandler is available -// in the global scope. -componentHandler.register({ - constructor: MaterialItem, - classAsString: 'MaterialItem', - cssClass: 'wsk-js-ripple-effect' -}); diff --git a/src/menu/_menu.scss b/src/menu/_menu.scss new file mode 100644 index 00000000..74528097 --- /dev/null +++ b/src/menu/_menu.scss @@ -0,0 +1,174 @@ +@import "../typography/typography"; +@import "../animation/animation"; +@import "../colors"; +@import "../shadow/shadow"; +@import "../ripple/ripple"; + +$menu-expand-duration: 0.3s; +$menu-fade-duration: 0.2s; + +.wsk-menu__container { + display: block; + margin: 0; + padding: 0; + border: none; + position: absolute; + overflow: visible; + height: 0; + width: 0; +} + +.wsk-menu__outline { + display: block; + background: $default-dropdown-bg-color; + margin: 0; + padding: 0; + border: none; + border-radius: 2px; + position: absolute; + top: 0; + left: 0; + overflow: hidden; + opacity: 0; + transform: scale(0); + transform-origin: 0 0; + @include shadow-z1(); + will-change: transform; + transition: transform $menu-expand-duration $animation-curve-default, + opacity $menu-fade-duration $animation-curve-default; + + .wsk-menu__container.is-visible & { + opacity: 1; + transform: scale(1); + } + + &.wsk-menu--bottom-right { + transform-origin: 100% 0; + } + + &.wsk-menu--top-left { + transform-origin: 0 100%; + } + + &.wsk-menu--top-right { + transform-origin: 100% 100%; + } +} + +.wsk-menu { + position: absolute; + list-style: none; + top: 0; + left: 0; + height: auto; + width: auto; + min-width: 124px; + padding: 8px 0; + margin: 0; + opacity: 0; + clip: rect(0 0 0 0); + + .wsk-menu__container.is-visible & { + opacity: 1; + } + + &.is-animating { + transition: opacity $menu-fade-duration $animation-curve-default, + clip $menu-expand-duration $animation-curve-default; + } + + &.wsk-menu--bottom-right { + left: auto; + right: 0; + } + + &.wsk-menu--top-left { + top: auto; + bottom: 0; + } + + &.wsk-menu--top-right { + top: auto; + left: auto; + bottom: 0; + right: 0; + } + + &.wsk-menu--unaligned { + top: auto; + left: auto; + } +} + +.wsk-menu__item { + display: block; + border: none; + color: $default-item-text-color; + background-color: transparent; + text-align: left; + margin: 0; + padding: 0 16px; + outline-color: $default-item-outline-color; + position: relative; + overflow: hidden; + @include typo-body-1(); + text-decoration: none; + cursor: pointer; + height: 48px; + line-height: 48px; + white-space: nowrap; + opacity: 0; + transition: opacity $menu-fade-duration $animation-curve-default; + user-select: none; + + .wsk-menu__container.is-visible & { + opacity: 1; + } + + &::-moz-focus-inner { + border: 0; + } + + &[disabled] { + color: $disabled-item-text-color; + background-color: transparent; + cursor: auto; + + &:hover { + background-color: transparent; + } + + &:focus { + background-color: transparent; + } + + & .wsk-ripple { + background: transparent; + } + } + + &:hover { + background-color: $default-item-hover-bg-color; + } + + &:focus { + outline: none; + background-color: $default-item-focus-bg-color; + } + + &:active { + background-color: $default-item-active-bg-color; + } +} + + +.wsk-menu__item--ripple-container { + display: block; + height: 100%; + left: 0px; + position: absolute; + top: 0px; + width: 100%; + z-index: 0; + overflow: hidden; +} diff --git a/src/menu/demo.html b/src/menu/demo.html new file mode 100644 index 00000000..0399d90b --- /dev/null +++ b/src/menu/demo.html @@ -0,0 +1,72 @@ + + + + + + + + Menu + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + diff --git a/src/menu/demo.scss b/src/menu/demo.scss new file mode 100644 index 00000000..2fd7c61e --- /dev/null +++ b/src/menu/demo.scss @@ -0,0 +1,33 @@ +@import "../styleguide_demo_bp"; +@import "../button/button"; +@import "../icons/icons"; +@import "_menu"; + +.PreviewBlock { + position: relative; + height: 500px; +} + +.demo-button-left { + position: absolute; + left: 24px; + top: 250px; +} + +.demo-button-left2 { + position: absolute; + left: 72px; + top: 250px; +} + +.demo-button-right { + position: absolute; + right: 24px; + top: 250px; +} + +.demo-button-right2 { + position: absolute; + right: 72px; + top: 250px; +} diff --git a/src/menu/menu.js b/src/menu/menu.js new file mode 100644 index 00000000..0aa75934 --- /dev/null +++ b/src/menu/menu.js @@ -0,0 +1,448 @@ +/** + * Class constructor for dropdown WSK component. + * Implements WSK component design pattern defined at: + * https://github.com/jasonmayes/wsk-component-design-pattern + * @param {HTMLElement} element The element that will be upgraded. + */ +function MaterialMenu(element) { + 'use strict'; + + this.element_ = element; + + // Initialize instance. + this.init(); +} + +/** + * Store constants in one place so they can be updated easily. + * @enum {string | number} + * @private + */ +MaterialMenu.prototype.Constant_ = { + // Total duration of the menu animation. + TRANSITION_DURATION_SECONDS: 0.3, + // The fraction of the total duration we want to use for menu item animations. + TRANSITION_DURATION_FRACTION: 0.8, + // How long the menu stays open after choosing an option (so the user can see + // the ripple). + CLOSE_TIMEOUT: 150 +}; + +/** + * Keycodes, for code readability. + * @enum {number} + * @private + */ +MaterialMenu.prototype.Keycodes_ = { + ENTER: 13, + ESCAPE: 27, + SPACE: 32, + UP_ARROW: 38, + DOWN_ARROW: 40 +}; + +/** + * Store strings for class names defined by this component that are used in + * JavaScript. This allows us to simply change it in one place should we + * decide to modify at a later date. + * @enum {string} + * @private + */ +MaterialMenu.prototype.CssClasses_ = { + CONTAINER: 'wsk-menu__container', + OUTLINE: 'wsk-menu__outline', + ITEM: 'wsk-menu__item', + ITEM_RIPPLE_CONTAINER: 'wsk-menu__item-ripple-container', + RIPPLE_EFFECT: 'wsk-js-ripple-effect', + RIPPLE_IGNORE_EVENTS: 'wsk-js-ripple-effect--ignore-events', + RIPPLE: 'wsk-ripple', + // Statuses + IS_UPGRADED: 'is-upgraded', + IS_VISIBLE: 'is-visible', + IS_ANIMATING: 'is-animating', + // Alignment options + BOTTOM_LEFT: 'wsk-menu--bottom-left', // This is the default. + BOTTOM_RIGHT: 'wsk-menu--bottom-right', + TOP_LEFT: 'wsk-menu--top-left', + TOP_RIGHT: 'wsk-menu--top-right', + UNALIGNED: 'wsk-menu--unaligned' +}; + +/** + * Initialize element. + */ +MaterialMenu.prototype.init = function() { + 'use strict'; + + if (this.element_) { + // Create container for the menu. + var container = document.createElement('div'); + container.classList.add(this.CssClasses_.CONTAINER); + this.element_.parentElement.insertBefore(container, this.element_); + this.element_.parentElement.removeChild(this.element_); + container.appendChild(this.element_); + this.container_ = container; + + // Create outline for the menu (shadow and background). + var outline = document.createElement('div'); + outline.classList.add(this.CssClasses_.OUTLINE); + this.outline_ = outline; + container.insertBefore(outline, this.element_); + + // Find the "for" element and bind events to it. + var forElId = this.element_.getAttribute('for'); + var forEl = null; + if (forElId) { + forEl = document.getElementById(forElId); + if (forEl) { + this.forElement_ = forEl; + forEl.addEventListener('click', this.handleForClick_.bind(this)); + forEl.addEventListener('keydown', + this.handleForKeyboardEvent_.bind(this)); + } + } + + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); + + for (var i = 0; i < items.length; i++) { + // Add a listener to each menu item. + items[i].addEventListener('click', this.handleItemClick_.bind(this)); + // Add a tab index to each menu item. + items[i].tabIndex = '-1'; + // Add a keyboard listener to each menu item. + items[i].addEventListener('keydown', + this.handleItemKeyboardEvent_.bind(this)); + } + + // Add ripple classes to each item, if the user has enabled ripples. + if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { + this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); + + for (i = 0; i < items.length; i++) { + var item = items[i]; + + var rippleContainer = document.createElement('span'); + rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER); + + var ripple = document.createElement('span'); + ripple.classList.add(this.CssClasses_.RIPPLE); + rippleContainer.appendChild(ripple); + + item.appendChild(rippleContainer); + item.classList.add(this.CssClasses_.RIPPLE_EFFECT); + } + } + + // Copy alignment classes to the container, so the outline can use them. + if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) { + this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT); + } + if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) { + this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT); + } + if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { + this.outline_.classList.add(this.CssClasses_.TOP_LEFT); + } + if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { + this.outline_.classList.add(this.CssClasses_.TOP_RIGHT); + } + if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { + this.outline_.classList.add(this.CssClasses_.UNALIGNED); + } + + container.classList.add(this.CssClasses_.IS_UPGRADED); + } +}; + +/** + * Handles a click on the "for" element, by positioning the menu and then + * toggling it. + * @private + */ +MaterialMenu.prototype.handleForClick_ = function(evt) { + 'use strict'; + + if (this.element_ && this.forElement_) { + var rect = this.forElement_.getBoundingClientRect(); + var forRect = this.forElement_.parentElement.getBoundingClientRect(); + + if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { + // Do not position the menu automatically. Requires the developer to + // manually specify position. + } else if (this.element_.classList.contains( + this.CssClasses_.BOTTOM_RIGHT)) { + // Position below the "for" element, aligned to its right. + this.container_.style.right = (forRect.right - rect.right) + 'px'; + this.container_.style.top = + this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px'; + } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { + // Position above the "for" element, aligned to its left. + this.container_.style.left = this.forElement_.offsetLeft + 'px'; + this.container_.style.bottom = (forRect.bottom - rect.top) + 'px'; + } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { + // Position above the "for" element, aligned to its right. + this.container_.style.right = (forRect.right - rect.right) + 'px'; + this.container_.style.bottom = (forRect.bottom - rect.top) + 'px'; + } else { + // Default: position below the "for" element, aligned to its left. + this.container_.style.left = this.forElement_.offsetLeft + 'px'; + this.container_.style.top = + this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px'; + } + } + + this.toggle(evt); +}; + +/** + * Handles a keyboard event on the "for" element. + * @private + */ +MaterialMenu.prototype.handleForKeyboardEvent_ = function(evt) { + 'use strict'; + + if (this.element_ && this.container_ && this.forElement_) { + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + + ':not([disabled])'); + + if (items && items.length > 0 && + this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { + if (evt.keyCode === this.Keycodes_.UP_ARROW) { + evt.preventDefault(); + items[items.length - 1].focus(); + } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) { + evt.preventDefault(); + items[0].focus(); + } + } + } +}; + +/** + * Handles a keyboard event on an item. + * @private + */ +MaterialMenu.prototype.handleItemKeyboardEvent_ = function(evt) { + 'use strict'; + + if (this.element_ && this.container_) { + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + + ':not([disabled])'); + + if (items && items.length > 0 && + this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { + var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target); + + if (evt.keyCode === this.Keycodes_.UP_ARROW) { + evt.preventDefault(); + if (currentIndex > 0) { + items[currentIndex - 1].focus(); + } else { + items[items.length - 1].focus(); + } + } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) { + evt.preventDefault(); + if (items.length > currentIndex + 1) { + items[currentIndex + 1].focus(); + } else { + items[0].focus(); + } + } else if (evt.keyCode === this.Keycodes_.SPACE || + evt.keyCode === this.Keycodes_.ENTER) { + evt.preventDefault(); + // Send mousedown and mouseup to trigger ripple. + var e = new MouseEvent('mousedown'); + evt.target.dispatchEvent(e); + e = new MouseEvent('mouseup'); + evt.target.dispatchEvent(e); + // Send click. + evt.target.click(); + } else if (evt.keyCode === this.Keycodes_.ESCAPE) { + evt.preventDefault(); + this.hide(); + } + } + } +}; + +/** + * Handles a click event on an item. + * @private + */ +MaterialMenu.prototype.handleItemClick_ = function(evt) { + 'use strict'; + + if (evt.target.getAttribute('disabled') !== null) { + evt.stopPropagation(); + } else { + // Wait some time before closing menu, so the user can see the ripple. + this.closing_ = true; + window.setTimeout(function(evt) { + this.hide(); + this.closing_ = false; + }.bind(this), this.Constant_.CLOSE_TIMEOUT); + } +}; + +/** + * Calculates the initial clip (for opening the menu) or final clip (for closing + * it), and applies it. This allows us to animate from or to the correct point, + * that is, the point it's aligned to in the "for" element. + * @private + */ +MaterialMenu.prototype.applyClip_ = function(height, width) { + 'use strict'; + + if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { + // Do not clip. + this.element_.style.clip = null; + } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) { + // Clip to the top right corner of the menu. + this.element_.style.clip = + 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)'; + } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { + // Clip to the bottom left corner of the menu. + this.element_.style.clip = + 'rect(' + height + 'px 0 ' + height + 'px 0)'; + } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { + // Clip to the bottom right corner of the menu. + this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' + + height + 'px ' + width + 'px)'; + } else { + // Default: do not clip (same as clipping to the top left corner). + this.element_.style.clip = null; + } +}; + +/** + * Adds an event listener to clean up after the animation ends. + * @private + */ +MaterialMenu.prototype.addAnimationEndListener_ = function() { + 'use strict'; + + var cleanup = function() { + this.element_.classList.remove(this.CssClasses_.IS_ANIMATING); + }.bind(this); + + // Remove animation class once the transition is done. + this.element_.addEventListener('transitionend', cleanup); + this.element_.addEventListener('webkitTransitionEnd', cleanup); +}; + +/** + * Displays the menu. + * @public + */ +MaterialMenu.prototype.show = function(evt) { + 'use strict'; + + if (this.element_ && this.container_ && this.outline_) { + // Measure the inner element. + var height = this.element_.getBoundingClientRect().height; + var width = this.element_.getBoundingClientRect().width; + + // Apply the inner element's size to the container and outline. + this.container_.style.width = width + 'px'; + this.container_.style.height = height + 'px'; + this.outline_.style.width = width + 'px'; + this.outline_.style.height = height + 'px'; + + var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS * + this.Constant_.TRANSITION_DURATION_FRACTION; + + // Calculate transition delays for individual menu items, so that they fade + // in one at a time. + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); + for (var i = 0; i < items.length; i++) { + var itemDelay = null; + if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) || + this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { + itemDelay = ((height - items[i].offsetTop - items[i].offsetHeight) / + height * transitionDuration) + 's'; + } else { + itemDelay = (items[i].offsetTop / height * transitionDuration) + 's'; + } + items[i].style.transitionDelay = itemDelay; + } + + // Apply the initial clip to the text before we start animating. + this.applyClip_(height, width); + + // Wait for the next frame, turn on animation, and apply the final clip. + // Also make it visible. This triggers the transitions. + window.requestAnimFrame(function() { + this.element_.classList.add(this.CssClasses_.IS_ANIMATING); + this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)'; + this.container_.classList.add(this.CssClasses_.IS_VISIBLE); + }.bind(this)); + + // Clean up after the animation is complete. + this.addAnimationEndListener_(); + + // Add a click listener to the document, to close the menu. + var callback = function(e) { + // Check to see if the document is processing the same event that + // displayed the menu in the first place. If so, do nothing. + // Also check to see if the menu is in the process of closing itself, and + // do nothing in that case. + if (e !== evt && !this.closing_) { + document.removeEventListener('click', callback); + this.hide(); + } + }.bind(this); + document.addEventListener('click', callback); + } +}; + +/** + * Hides the menu. + * @public + */ +MaterialMenu.prototype.hide = function(evt) { + 'use strict'; + + if (this.element_ && this.container_ && this.outline_) { + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); + + // Remove all transition delays; menu items fade out concurrently. + for (var i = 0; i < items.length; i++) { + items[i].style.transitionDelay = null; + } + + // Measure the inner element. + var height = this.element_.getBoundingClientRect().height; + var width = this.element_.getBoundingClientRect().width; + + // Turn on animation, and apply the final clip. Also make invisible. + // This triggers the transitions. + this.element_.classList.add(this.CssClasses_.IS_ANIMATING); + this.applyClip_(height, width); + this.container_.classList.remove(this.CssClasses_.IS_VISIBLE); + + // Clean up after the animation is complete. + this.addAnimationEndListener_(); + } +}; + +/** + * Displays or hides the menu, depending on current state. + * @public + */ +MaterialMenu.prototype.toggle = function(evt) { + 'use strict'; + + if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { + this.hide(evt); + } else { + this.show(evt); + } +}; + +// The component registers itself. It can assume componentHandler is available +// in the global scope. +componentHandler.register({ + constructor: MaterialMenu, + classAsString: 'MaterialMenu', + cssClass: 'wsk-js-menu' +}); diff --git a/src/styleguide.scss b/src/styleguide.scss index 65daeadc..ad352cbc 100644 --- a/src/styleguide.scss +++ b/src/styleguide.scss @@ -11,11 +11,10 @@ @import "card/card"; @import "checkbox/checkbox"; @import "column-layout/column-layout"; -@import "dropdown-menu/dropdown-menu"; @import "footer/mega_footer"; @import "footer/mini_footer"; @import "icon-toggle/icon-toggle"; -@import "item/item"; +@import "menu/menu"; @import "layout/layout"; @import "list/list"; @import "radio/radio";