DEV Community

Cover image for Dropdown Component for main menu ~ Laravel/Breeze ~ Tailwind
Lavinia
Lavinia

Posted on

 

Dropdown Component for main menu ~ Laravel/Breeze ~ Tailwind

Currently, I'm working on a project using Laravel, I haven't used Laravel since version 5, I think, also I stopped programming for about a year for different personal reasons.

The thing is that a lot of things have changed and one of them is that now Laravel has this package to manage the authentication process: Laravel/Breeze.

Puedes leer la versión en Español en mi blog.

I had some issues at the beginning because I didn't have the latest version of PHP, but after I solved them I was able to focus on the next steps. Tailwind is something really new for me and Laravel/Breeze is working with templates and styles using that library, but one thing that is missing is the "sub-menu" for the main links.

The menu has the option to have a drop-down for the user actions on the right side:
Laravel Breeze Menu

For the project I'm working on, I need to have a sub-menu for some of the main menu items:
Laravel Breeze with Menu and Sub Menu

Components

Following the same logic of the original components, I've created two components:

nav-link-parent.blade.php

@props(['active'])

@php
$classes = ($active ?? false)
            ? 'parent-nav inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-semibold leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out cursor-pointer relative'
            : 'parent-nav inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-semibold leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out cursor-pointer relative';
@endphp

<div x-data="{ open: false }" @click.away="open = false" @close.stop="open = false" @click="open = ! open" {{ $attributes->merge(['class' => $classes]) }} >
    <div>
        {{ $name }}

        <div class="ml-1 inline-block relative top-1">
            <svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
                <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
            </svg>
        </div>
    </div>

    <div class="children border border-gray-300" 
    x-show="open"
    x-transition:enter="transition ease-out duration-200"
    x-transition:enter-start="transform opacity-0 scale-95"
    x-transition:enter-end="transform opacity-100 scale-100"
    x-transition:leave="transition ease-in duration-75"
    x-transition:leave-start="transform opacity-100 scale-100"
    x-transition:leave-end="transform opacity-0 scale-95">
        {{ $children }}
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

responsive-nav-link-parent.blade.php

@props(['active'])

@php
$classes = ($active ?? false)
            ? 'parent-nav inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-semibold leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
            : 'parent-nav inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-semibold leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out';
@endphp

@props(['active'])

@php
$classes = ($active ?? false)
            ? 'parent-nav block pl-3 pr-4 py-2 border-l-4 border-indigo-400 text-base font-semibold text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out'
            : 'parent-nav block pl-3 pr-4 py-2 border-l-4 border-transparent text-base font-semibold text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out';
@endphp

<div x-data="{ open: false }" @click.away="open = false" @close.stop="open = false" @click="open = ! open" {{ $attributes->merge(['class' => $classes]) }} >
    <div>
        {{ $name }}

        <div class="ml-1 inline-block relative top-1">
            <svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
                <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
            </svg>
        </div>
    </div>

    <div class="children border border-gray-300 mt-3" 
    x-show="open"
    x-transition:enter="transition ease-out duration-200"
    x-transition:enter-start="transform opacity-0 scale-95"
    x-transition:enter-end="transform opacity-100 scale-100"
    x-transition:leave="transition ease-in duration-75"
    x-transition:leave-start="transform opacity-100 scale-100"
    x-transition:leave-end="transform opacity-0 scale-95">
        {{ $children }}
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Style

Some CSS to set the right position for the elements.

app.css

.children {
    background: #fff;
}

.children a,
.children .separator {
    display: block;
    margin: 5px 0px;
    padding: 5px 10px;
}

.children .separator {
    border-top-width: 2px;
}

.children a:hover {
    color: #818cf8;
}

@media (min-width: 768px) {
    .children {
        position: absolute;
        width: 155px;
        top: 70px;
    }
}
Enter fullscreen mode Exit fullscreen mode

Update the Navigation layout

And last I've updated the navigation layout and have added two slots: name and children

navigation.blade.php

...
<x-nav-link-parent :href="'#'" :active="request()->routeIs('padron.*')">
    <x-slot name="name">Option A</x-slot>
    <x-slot name="children">
        <a href="#">Item A</a>
        <span class="separator"></span>
        <a href="#">Item B</a>
        <a href="#">Item C</a>
        <span class="separator"></span>
        <a href="#">Item D</a>
    </x-slot>
</x-nav-link-parent>
...
<x-responsive-nav-link-parent :href="'#'" :active="request()->routeIs('padron.*')">
    <x-slot name="name">Padrón</x-slot>
    <x-slot name="children">
        <a href="#">Buscador</a>
        <span class="separator"></span>
        <a href="#">Centros de votacion</a>
        <a href="#">Juntas</a>
        <span class="separator"></span>
        <a href="#">Cartografia</a>
    </x-slot>
</x-responsive-nav-link-parent>
Enter fullscreen mode Exit fullscreen mode

Final Result

Menu and Sub Menu

Menu and Sub Menu, Responsive version

Top comments (3)

Collapse
 
arborrow profile image
Anthony Borrow, S.J.

Thanks for sharing. I wanted to implement something like this using Jetstream. Currently, it appears the parent is not clickable as a link. I can see some advantages to this but also some limitations. Ideally, I would think that clicking on the arrow would allow the options to appear but clicking on the word would take you to the parent link.

Collapse
 
brodev23 profile image
brodev23

Is tailwind working properly with laravel. terminology which i used Using Laravel Mix. some classes working but some are not. Tailwind is not working properly. Is all you faced the same problems.

laravelTailwindissues #everyonefacing

Collapse
 
zdiodic profile image
Dio

Thank for your sharing, helping me to learn tailwind

Why You Need to Study Javascript Fundamentals

The harsh reality for JS Developers: If you don't study the fundamentals, you'll be just another “Coder”. Top learnings on how to get to the mid/senior level faster as a JavaScript developer by Dragos Nedelcu.