Mobile-first responsive navigation menu design concept.
The structure of the links is just exactly the same as how I forked it. It is like, I just added some classes, and some elements, example for icons, or the text itself.
The menu and sub-menu was created with recursive function.
How I did it
Styling
First, using the default layout in HTML, I tried to style the navigation menu. There is no crucial part in styling, it is just how you want the navigation to look.
Rendering
Honestly, because I really want to use the GSAP, I brute-forced this one. So if there is anyone who has better code for this, please comment down below.
// This is the recursive function that will create the elements
createMenuJSX(menu = this.props.menu) {
// The array that will be rendered
let link = [];
for (let i in menu) {
let m = menu[i];
let ic = <i className="cpc-icon cpc-hidden fas fa-caret-down"></i>;
if (typeof m.icon !== 'undefined') {
ic = <i className={'cpc-icon ' + m.icon}></i>;
}
// if 'menu' object is undefined (if it doesn't have a sub menu),
// just show content.
if (typeof m.menu === 'undefined') {
// Note the useless elements here, this is to maintain balance
// between the texts and icons. I think there is a better way
// here, but I ran out of ideas, so I kept it like this.
link.push(
<li>
<a href={m.link}>
{ic}
<span>{i}</span>
<i className="cpc-caret cpc-hidden fas fa-caret-down"></i>
</a>
</li>
);
} else if (typeof m.menu === 'object') {
let tmpSubmenu = this.state.submenu;
let tmpLength = tmpSubmenu.length;
// Create a temporary array. This will be used later for
// rendering, as well as the 'ref' for GSAP animation.
tmpSubmenu.push({
'id': m.link,
'active': false,
'caret': React.createRef(),
'sub': React.createRef()
});
// The click event handler is here.
// The caret and sub menu ref is set here. As mentioned earlier
// I need this to use the GSAP as animation. If I would not use
// it, I can easily set the class, and will not use brute force
// in rendering these elements. I can directly put this method
// as rendering method in render() method.
link.push(
<li>
<a
href={m.link}
onClick={this.menuClickEvent.bind(this, tmpLength)}
>
{ic}
<span>{i}</span>
<i
className="cpc-caret fas fa-caret-down"
ref={tmpSubmenu[tmpLength].caret}
></i>
</a>
<ul className="cpc-sub" ref={tmpSubmenu[tmpLength].sub}>
{this.createMenuJSX(m.menu)}
</ul>
</li>
);
this.setState({submenu: tmpSubmenu});
}
}
return link;
}
// I used the created jsxData state to display the elements.
render() {
return (
<nav className="cpc-menu">
<ul className="cpc-main">
{this.state.jsxData}
</ul>
</nav>
);
}
Changing the state
Now, we already rendered the elements, let's go to the click events, to show the sub menu of the menu with a caret.
menuClickEvent(i) {
let submenu = this.state.submenu;
let tmpmenu = submenu[i];
// This is how you get the element that was set as a 'ref'
let sub = tmpmenu.sub.current;
let caret = tmpmenu.caret.current;
if (tmpmenu.active === false) {
tmpmenu.active = true;
// GSAP animation for caret. Notice that I used the ref
// for this one.
TweenLite.to(caret, 1, {
transform: 'rotate(180deg)',
ease: Elastic.easeOut.config(1, 0.3)
});
// GSAP animation for sub menu. Notice that I used the ref
// for this one.
TweenLite.to(sub, 1, {
height: sub.scrollHeight,
visibility: 'visible',
ease: Elastic.easeOut.config(1, 0.3)
});
} else {
tmpmenu.active = false;
// GSAP animation for caret. Notice that I used the ref
// for this one.
TweenLite.to(caret, 1, {
transform: 'rotate(0deg)',
ease: Elastic.easeOut.config(1, 0.3)
});
// GSAP animation for sub menu. Notice that I used the ref
// for this one.
TweenLite.to(sub, 0.5, {
height: 0,
ease: Bounce.easeOut
}).eventCallback('onComplete', () => {
// 'eventCallback' will be called after some events was called,
// like, start, stop, complete, etc. In my case, I used complete.
TweenLite.to(sub, 0, {
visibility: 'hidden'
})
});
}
submenu[i] = tmpmenu;
this.setState({submenu: submenu});
}
So, this is how this menu and sub-menu is working. If you have better suggestion for this one, please comment down below.
You'll notice that I coded it crappy. It is because I am not yet good in ReactJS. I'm still practicing it.
Conclusion
To conclude this challenge, it was fun, that I learned many things about react. While I'm coding, of course, I am reading the documentation. This is my first time to do the recursion in ReactJS. Well, it is just the same as usual recursion actually. I also want to know more about GSAP animation, because I've been coding the animation at CSS only.
Resources
Resources/Libraries/Frameworks:
Google Fonts: Montserrat
Font Awesome: Icons
ReactJS: Functionality and rendering
GSAP: Animation
Top comments (0)