This article covers creating a Riot Menu component, using the Material Design CSS BeerCSS, and executing an action on click events.
Before starting, make sure you have a base application running, or read my previous article Setup Riot + BeerCSS + Vite.
These articles form a series focusing on RiotJS paired with BeerCSS, designed to guide you through creating components and mastering best practices for building production-ready applications. I assume you have a foundational understanding of Riot; however, feel free to refer to the documentation if needed: https://riot.js.org/documentation/
A menu opens upon interaction with an element (such as an icon, button, or input field) or when users perform a specific action. The menu displays a list of choices on a temporary surface, it allows users to make a selection to execute actions.
Menu Component Base
The goal is to create a Riot app with a Menu appearing when a button is clicked, hide it when an item is clicked, and execute an action. Bonus: Hide the menu when a click happens outside the component.
To show a Menu when a button is clicked: The menu is part of the Button Component, as a Slot. The <slot>
Riot tag injects custom HTML templates in a child component from its parent.
The following Button Component code is used to make the Menu visible, located in the ./components/c-button.riot. The HTML comes from the BeerCSS documentation and I added RiotJS syntax for the logic:
<c-button> <button> <span><slot></slot></span> <i icon={ props?.icon }>props?.icon</i> <slot name="menu"></slot> </button> </c-button>
Click to learn more about creating Button Component with RiotJS
Let's break down the code:
- The
<c-button>
and</c-button>
define a custom root tag, with the same name as the file. You must write it; otherwise, it may create unexpected results. Using the<button></button>
as a root tag or redefining native HTML tags is a bad practice, starting withc-
is a good convention. - The button label is passed as Slot.
- The Menu is passed as a Named Slot "Menu":
<slot name="menu"></slot>
. - An optional icon can be passed as an attribute if props.icon exists, it will show a Google Font Icon
Finally, load and instantiate the c-button.riot in a front page named index.riot:
<index-riot> <div style="width:600px;padding:20px;"> <c-button icon="arrow_drop_down" onclick={ (ev) => toggle(ev, 'button') } onfocusout={ () => update({ active: false })}> Menu <template slot="menu"> <menu class="no-wrap{ state.active === true ? ' active' : ''}"> <a class="row" onclick={ (ev) => toggle(ev, 'item1') }> <i>visibility</i> <span class="max">Item 1</span> </a> <a class="row" onclick={ (ev) => toggle(ev, 'item2') }> <i>content_copy</i> <span class="max">Item 2</span> <span>⌘C</span> </a> <a class="row" onclick={ (ev) => toggle(ev, 'item3') }> <i>edit</i> <span class="max">Item 3</span> </a> <div class="small-divider"></div> <a class="row" onclick={ (ev) => toggle(ev, 'item4') }> <img class="circle tiny" src="../favicon.png"> <div class="max"> <div>Item 4</div> <label>Some text here</label> </div> </a> </menu> </template> </c-button> </div> <script> import cButton from "../components/c-button.riot" export default { components: { cButton }, state: { active: false }, toggle (ev, origin) { ev.stopPropagation(); ev.preventDefault(); // Hide the menu this.update({ active: this.state.active === true ? false : true }) if (origin === 'item1') { // do something } else if (origin === 'item2') { // do something else } } } </script> </index-riot>
Source Code: https://github.com/steevepay/riot-beercss/blob/main/examples/index.menu.riot
Code details:
- The component is imported with
import cButton from "./components/c-button.riot";
then loaded in the components:{} Riot object. - The
button
component is instantiated with<c-button>
on the HTML. - The menu is passed as a slot into a
<menu>
HTML tag - Each item of the list has the following architecture, wrapped in a
<a>
tag with an icon and label:<a class="row"><i>icon</i><span class="max">Label</span></a>
. - The state of the menu is stored into a state:{} Riot object, through the Boolean variable state.active.
- To make the menu visible, the menu must contain the class "active": When the state.active is true, it applies the class "active"; otherwise, it applies nothing.
- When a click occurs on the button, the function
toggle
is executed to assign the opposite Boolean to state.active. At the same time, a String is passed to thetoggle
function to define the origin of the click, either:button
, or an item of the menu. Thanks to the origin, a specific function can be executed: API calls, open a page, and any action! - When a click occurs outside the Menu, the event "focusout" is caught to hide the Menu with the expression:
onfocusout={ () => update({ active: false })}
. - An item on the Menu can have a different style, for example, the last item on the list prints an image instead of an icon, followed by a title and a subtitle. Find all Menu examples on the BeerCSS Menu documentation.
Menu Component Testing
It exists two methods for testing the Menu component, and it is covered in two different articles:
Conclusion
Voilà 🎉 We made a Menu Riot Component using Material Design elements with BeerCSS.
The source code of the Menu bar is available on Github:
https://github.com/steevepay/riot-beercss/blob/main/components/c-menu.riot
Have a great day! Cheers 🍻
Top comments (1)
Hey folks! Please share your questions in the comments section, I would be glad to help you with RiotJS! Have a great day! Cheers 🍻
Some comments may only be visible to logged-in visitors. Sign in to view all comments.