From c531b128f78a23ffff980696916d02b877c2389f Mon Sep 17 00:00:00 2001 From: Michael Rienstra Date: Thu, 10 Apr 2014 18:18:06 -0700 Subject: [PATCH] New "side-menus" component. Based on the `modals` component. See example at http://michaelrienstra.com/temporary/ratchet/side-menus.html The current implementation relies on putting a wrapper (`
`) around the `side-menu`s siblings (`
` & `
`). I have working code to handle the wrapping of siblings automatically, as well as working code that works without the wrapper (but I realized I was going to have to add more "moving bits" to get the subtle drop shadow to animate without the wrapper). Minimally tested on Android (Nexus 4) & iOS (iPhone 5). Happy to do more testing / etc. if this component is of interest. Attribution: I borrowed a few lines of CSS from http://mmenu.frebsite.nl/ (which is dual licensed under the MIT and GPL licenses). --- js/side-menus.js | 74 ++++++++++++++++++++++++++++++++++++++++++++ sass/side-menus.scss | 49 +++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 js/side-menus.js create mode 100644 sass/side-menus.scss diff --git a/js/side-menus.js b/js/side-menus.js new file mode 100644 index 0000000..9e4e199 --- /dev/null +++ b/js/side-menus.js @@ -0,0 +1,74 @@ +/* ======================================================================== + * Ratchet: side-menus.js v2.0.2 + * http://goratchet.com/components#sideMenus + * ======================================================================== + * Copyright 2014 Connor Sears + * Licensed under MIT (https://github.com/twbs/ratchet/blob/master/LICENSE) + * ======================================================================== */ + +!(function () { + 'use strict'; + + var findSideMenus = function (target) { + var i; + var sideMenus = document.querySelectorAll('a'); + + for (; target && target !== document; target = target.parentNode) { + for (i = sideMenus.length; i--;) { + if (sideMenus[i] === target) { + return target; + } + } + } + }; + + var getSideMenu = function (event) { + var sideMenuToggle = findSideMenus(event.target); + if (sideMenuToggle && sideMenuToggle.hash) { + var sideMenu = document.querySelector(sideMenuToggle.hash); + if (sideMenu && sideMenu.classList.contains('side-menu')) { + return sideMenu; + } + } + }; + + var getSideMenuSiblings = function (sideMenu) { + var siblings = [].slice.call(sideMenu.parentNode.children); + siblings = siblings.filter(function(val) { + return [sideMenu].indexOf(val) === -1 && val.nodeName !== 'SCRIPT'; + }); + return siblings; + }; + + window.addEventListener('touchend', function (event) { + var sideMenu = getSideMenu(event); + if (sideMenu) { + var siblings = getSideMenuSiblings(sideMenu); + var i; + var l; + var hasBlocker = false; + for (i = 0, l = siblings.length; i < l; i++) { + if (!hasBlocker && siblings[i].classList.contains('side-menu-blocker')) { + hasBlocker = true; + } + if (siblings[i].classList.contains('activeSideMenu')) { + siblings[i].classList.remove('activeSideMenu'); + siblings[i].classList.add('inactiveSideMenu'); + } else { + siblings[i].classList.remove('inactiveSideMenu'); + siblings[i].classList.add('activeSideMenu'); + } + } + if (hasBlocker === false) { + var blocker = document.createElement('a'); + blocker.href = '#' + sideMenu.id; + blocker.classList.add('side-menu-blocker'); + sideMenu.parentElement.appendChild(blocker); + siblings.push(blocker); + blocker.classList.add('activeSideMenu'); + } + + event.preventDefault(); // prevents rewriting url (apps can still use hash values in url) + } + }); +}()); diff --git a/sass/side-menus.scss b/sass/side-menus.scss new file mode 100644 index 0000000..5b7de39 --- /dev/null +++ b/sass/side-menus.scss @@ -0,0 +1,49 @@ +// +// Side menus +// -------------------------------------------------- + +.side-menu { + position: fixed; + top: 0; + z-index: -1; + width: 80%; + height: 100%; +} + +.side-menu-blocker { + background: url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==) transparent; + display: none; + width: 100%; + height: 100%; + position: fixed; + top: 0; + z-index: 999999; + + &.activeSideMenu { + display: block; + box-shadow: none; + } +} + +.side-menu-siblings-wrapper { + width: 100%; + height: 100%; + /* Alternate approach, as per BODY: + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + */ +} + +.inactiveSideMenu, .activeSideMenu { + box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); + -webkit-transition: -webkit-transform .4s; + -moz-transition: -moz-transform .4s; + transition: transform .4s; + @include transform(translate3d(0, 0, 0)); +} +.activeSideMenu { + @include transform(translate3d(80%, 0, 0)); +}