Skip to content

Commit

Permalink
Updated to v2.0
Browse files Browse the repository at this point in the history
- Based on the CodyHouse Framework
- SCSS and JS refectoring

Co-Authored-By: Sebastiano Guerriero <[email protected]>
  • Loading branch information
claudia-romano and sebastiano-guerriero committed Apr 15, 2019
1 parent aaa363c commit ebb0057
Show file tree
Hide file tree
Showing 16 changed files with 434 additions and 699 deletions.
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
Multi-Level Accordion Menu
=========
# Multi-Level Accordion Menu

A simple CSS accordion menu with support for sub level items.

[Article on CodyHouse](https://codyhouse.co/gem/css-multi-level-accordion-menu)

[Demo](https://codyhouse.co/demo/multi-level-accordion-menu/index.html)
[Demo](https://codyhouse.co/demo/multi-level-accordion-menu)

[License](https://codyhouse.co/license)

## Dependencies

This experiment is built upon the [CodyHouse Framework](https://github.com/CodyHouse/codyhouse-framework).

Make sure to include both the _global.scss and util.js files of the framework.

## Credits

Icons: [Nucleo](https://nucleoapp.com/)

[Terms](https://codyhouse.co/terms/)
Icons: [Nucleo Library](https://nucleoapp.com/)
1 change: 1 addition & 0 deletions assets/css/style.css

Large diffs are not rendered by default.

146 changes: 146 additions & 0 deletions assets/css/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
@import '../../../../codyhouse-framework/main/assets/css/_global.scss'; // ⚠️ make sure to import the CodyHouse framework
@import url('https://fonts.googleapis.com/css?family=Jaldi:400,700'); // custom font

// --------------------------------

// Multi-Level Accordion Menu - by CodyHouse.co

// --------------------------------

:root {
// colors
@include defineColorHSL(--cd-color-1, 218, 7%, 32%); // Abbey
@include defineColorHSL(--cd-color-2, 127, 83%, 80%); // Gossip

// font
--font-primary: 'Jaldi', sans-serif;
}

body {
background-color: var(--cd-color-2);
}

.cd-article-link {
color: rgba(black, 0.5);
}

h1 {
font-weight: bold;
color: rgba(black, 0.9);
}

.cd-accordion {
background: var(--cd-color-1);
@include fontSmooth;
box-shadow: var(--shadow-lg);
}

.cd-accordion--animated .cd-accordion__label::before {
transition: transform .3s;
}

.cd-accordion__sub {
display: none; // by default hide all sub menus
overflow: hidden;
}

.cd-accordion__sub--is-visible {
display: block;
}

.cd-accordion__item {
user-select: none;
}

.cd-accordion__input { // hide native checkbox
position: absolute;
opacity: 0;
}

.cd-accordion__label {
position: relative;
display: flex;
align-items: center;
padding: var(--space-sm) var(--space-md);
background: var(--cd-color-1);
--color-shadow: lightness(var(--cd-color-1), 1.2);
box-shadow: inset 0 -1px var(--color-shadow);
color: var(--color-white);

span {
order: 3;
}

&:hover {
background: lightness(var(--cd-color-1), 1.1);
}
}

// icons
.cd-accordion__label::after, .cd-accordion__label--icon-folder::before {
content: '';
display: block;
width: 16px;
height: 16px;
background-image: url('../img/cd-icons.svg');
background-repeat: no-repeat;
margin-right: var(--space-xxxs);
}

.cd-accordion__label--icon-folder::before {
order: 1;
}

.cd-accordion__label::after {
order: 2;
}

.cd-accordion__label--icon-folder {
&::before { // arrow icon
background-position: 0 0;
transform: rotate(-90deg);
}

&::after { // folder icon
background-position: -16px 0;
}
}

.cd-accordion__label--icon-img::after { // image icon
background-position: -48px 0;
}

.cd-accordion__input:checked + .cd-accordion__label::before { // rotate arrow
transform: rotate(0);
}

.cd-accordion__input:checked + .cd-accordion__label::after { // show open folder icon if item is checked
background-position: -32px 0;
}

.cd-accordion__input:checked ~ .cd-accordion__sub { // show children when item is checked
display: block;
}

.cd-accordion__sub--l1 .cd-accordion__label {
background: lightness(var(--cd-color-1), 0.65);
--color-shadow: lightness(var(--cd-color-1), 0.85);
box-shadow: inset 0 -1px var(--color-shadow);
padding-left: calc(var(--space-md) + 16px);

&:hover {
background: lightness(var(--cd-color-1), 0.75);
}
}

.cd-accordion__item:last-child .cd-accordion__label {
box-shadow: none;
}

.cd-accordion__sub--l2 .cd-accordion__label {
padding-left: calc(var(--space-md) + var(--space-xxxs) + 32px);
}

.cd-accordion__sub--l3 .cd-accordion__label {
padding-left: calc(var(--space-md) + var(--space-xxxs) + 48px);
}
File renamed without changes
27 changes: 27 additions & 0 deletions assets/js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
(function() {
// Multi-Level Accordion Menu - by CodyHouse.co
var accordionsMenu = document.getElementsByClassName('cd-accordion--animated');

if( accordionsMenu.length > 0 && window.requestAnimationFrame) {
for(var i = 0; i < accordionsMenu.length; i++) {(function(i){
accordionsMenu[i].addEventListener('change', function(event){
animateAccordion(event.target);
});
})(i);}

function animateAccordion(input) {
var bool = input.checked,
dropdown = input.parentNode.getElementsByClassName('cd-accordion__sub')[0];

Util.addClass(dropdown, 'cd-accordion__sub--is-visible'); // make sure subnav is visible while animating height

var initHeight = !bool ? dropdown.offsetHeight: 0,
finalHeight = !bool ? 0 : dropdown.offsetHeight;

Util.setHeight(initHeight, finalHeight, dropdown, 200, function(){
Util.removeClass(dropdown, 'cd-accordion__sub--is-visible');
dropdown.removeAttribute('style');
});
}
}
}());
174 changes: 174 additions & 0 deletions assets/js/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// Utility function
function Util () {};

/*
class manipulation functions
*/
Util.hasClass = function(el, className) {
if (el.classList) return el.classList.contains(className);
else return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
};

Util.addClass = function(el, className) {
var classList = className.split(' ');
if (el.classList) el.classList.add(classList[0]);
else if (!Util.hasClass(el, classList[0])) el.className += " " + classList[0];
if (classList.length > 1) Util.addClass(el, classList.slice(1).join(' '));
};

Util.removeClass = function(el, className) {
var classList = className.split(' ');
if (el.classList) el.classList.remove(classList[0]);
else if(Util.hasClass(el, classList[0])) {
var reg = new RegExp('(\\s|^)' + classList[0] + '(\\s|$)');
el.className=el.className.replace(reg, ' ');
}
if (classList.length > 1) Util.removeClass(el, classList.slice(1).join(' '));
};

Util.toggleClass = function(el, className, bool) {
if(bool) Util.addClass(el, className);
else Util.removeClass(el, className);
};

Util.setAttributes = function(el, attrs) {
for(var key in attrs) {
el.setAttribute(key, attrs[key]);
}
};

/*
DOM manipulation
*/
Util.getChildrenByClassName = function(el, className) {
var children = el.children,
childrenByClass = [];
for (var i = 0; i < el.children.length; i++) {
if (Util.hasClass(el.children[i], className)) childrenByClass.push(el.children[i]);
}
return childrenByClass;
};

/*
Animate height of an element
*/
Util.setHeight = function(start, to, element, duration, cb) {
var change = to - start,
currentTime = null;

var animateHeight = function(timestamp){
if (!currentTime) currentTime = timestamp;
var progress = timestamp - currentTime;
var val = parseInt((progress/duration)*change + start);
element.setAttribute("style", "height:"+val+"px;");
if(progress < duration) {
window.requestAnimationFrame(animateHeight);
} else {
cb();
}
};

//set the height of the element before starting animation -> fix bug on Safari
element.setAttribute("style", "height:"+start+"px;");
window.requestAnimationFrame(animateHeight);
};

/*
Smooth Scroll
*/

Util.scrollTo = function(final, duration, cb) {
var start = window.scrollY || document.documentElement.scrollTop,
currentTime = null;

var animateScroll = function(timestamp){
if (!currentTime) currentTime = timestamp;
var progress = timestamp - currentTime;
if(progress > duration) progress = duration;
var val = Math.easeInOutQuad(progress, start, final-start, duration);
window.scrollTo(0, val);
if(progress < duration) {
window.requestAnimationFrame(animateScroll);
} else {
cb && cb();
}
};

window.requestAnimationFrame(animateScroll);
};

/*
Focus utility classes
*/

//Move focus to an element
Util.moveFocus = function (element) {
if( !element ) element = document.getElementsByTagName("body")[0];
element.focus();
if (document.activeElement !== element) {
element.setAttribute('tabindex','-1');
element.focus();
}
};

/*
Misc
*/

Util.getIndexInArray = function(array, el) {
return Array.prototype.indexOf.call(array, el);
};

Util.cssSupports = function(property, value) {
if('CSS' in window) {
return CSS.supports(property, value);
} else {
var jsProperty = property.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase();});
return jsProperty in document.body.style;
}
};

/*
Polyfills
*/
//Closest() method
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
}

if (!Element.prototype.closest) {
Element.prototype.closest = function(s) {
var el = this;
if (!document.documentElement.contains(el)) return null;
do {
if (el.matches(s)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
};
}

//Custom Event() constructor
if ( typeof window.CustomEvent !== "function" ) {

function CustomEvent ( event, params ) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent( 'CustomEvent' );
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
return evt;
}

CustomEvent.prototype = window.Event.prototype;

window.CustomEvent = CustomEvent;
}

/*
Animation curves
*/
Math.easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
Loading

0 comments on commit ebb0057

Please sign in to comment.