Dropdown

Dropdown powered by ALpineJS and Laravel Blade View Components.

Usage

// in blade view

<x-dropdown alignment="right">
    <x-slot name="trigger">
        <button class="relative w-8 h-8 text-gray-600 rounded-full border bg-gray-100 font-semibold focus:outline-none focus:shadow-outline text-sm overflow-hidden">
            <img src="{{ auth()->user()->avatarUrl() }}" alt="Profile Photo" class="absolute inset-0 h-full w-full object-cover">
        </button>
    </x-slot>

    <div class="text-gray-600 text-sm truncate px-4 py-2">Hi, {{ auth()->user()->name }}</div>

    <x-dropdown-item to="{{ route('dashboard') }}" class="flex items-center">
        <span class="flex-shrink-0 w-5 h-5 mr-2 text-gray-500">@svg(url('/cms/icons/clipboard-list.svg'))</span> Dashboard
    </x-dropdown-item>
    <x-dropdown-item to="{{ route('contacts') }}" class="flex items-center">
        <span class="flex-shrink-0 w-5 h-5 mr-2 text-gray-500">@svg(url('/cms/icons/annotation.svg'))</span> Contacts
    </x-dropdown-item>
    <x-dropdown-item to="{{ route('articles.create') }}" class="flex items-center">
        <span class="flex-shrink-0 w-5 h-5 mr-2 text-gray-500">@svg(url('/cms/icons/pencil-alt.svg'))</span> New Article
    </x-dropdown-item>
    <x-dropdown-item to="{{ route('profile') }}" class="flex items-center">
        <span class="flex-shrink-0 w-5 h-5 mr-2 text-gray-500">@svg(url('/cms/icons/user.svg'))</span> Profile
    </x-dropdown-item>
    <x-dropdown-item to="{{ route('settings') }}" class="flex items-center">
        <span class="flex-shrink-0 w-5 h-5 mr-2 text-gray-500">@svg(url('/cms/icons/cog.svg'))</span> Settings
    </x-dropdown-item>
    <x-dropdown-item to="#" class="flex items-center" onclick="event.preventDefault(); document.getElementById('js-logout').submit()">
        <span class="flex-shrink-0 w-5 h-5 mr-2 text-gray-500">@svg(url('/cms/icons/logout.svg'))</span> Log out
    </x-dropdown-item>

    <x-form method="POST" action="{{ route('logout') }}" id="js-logout"></x-form>
</x-dropdown>

Component

// components/dropdown.blade.php

@props(['alignment' => 'left'])

@php
    $alignmentClasses = [
        'left' => 'left-0',
        'right' => 'right-0',
    ];   
@endphp

<div x-data="{ open: false }" class="relative" x-cloak>
    <div x-on:click="open = ! open" class="cursor-pointer">
        {{ $trigger }}
    </div>

    <div
        x-show="open"
        x-transition:enter="transition ease-out duration-300"
        x-transition:enter-start="opacity-0 transform scale-95"
        x-transition:enter-end="opacity-100 transform scale-100"
        x-transition:leave="transition ease-in duration-200"
        x-transition:leave-start="opacity-100 transform scale-100"
        x-transition:leave-end="opacity-0 transform scale-95"
        @click.away="open = false"
        class="absolute {{ $alignmentClasses[$alignment] }} w-40 z-20 shadow-xs shadow-lg overflow-hidden rounded-lg p-1 bg-white mt-2 -mr-1"
    >   
        {{ $slot }}
    </div>
</div>
// components/dropdown-item.blade.php

@php
    $navlinkActive = \Illuminate\Support\Str::startsWith(request()->url(), $to) 
        ? 'bg-indigo-100 text-indigo-600' 
        : 'text-gray-700';
@endphp

<a 
    href="{{ $to }}" 
    {{ 
        $attributes->merge([
            'class' => 'truncate mb-1 rounded-lg px-4 py-1 block hover:bg-indigo-100 transition duration-200 ease-out '. $navlinkActive
        ])
    }}>
    {{ $slot }}
</a>
// components/form.php

@props([
    'method' => 'POST',
    'action'
])

<form 
    action="{{ $action }}" 
    method="{{ $method === 'GET' ? 'GET' : 'POST' }}"
    {{ $attributes }}
    onsubmit="
        event.submitter.disabled = true; 
        event.submitter.classList.add(
            'base-spinner', 
            'cursor-not-allowed', 
            'opacity-75'
        )
    ">
    @csrf

    @if (!in_array($method, ['GET', 'POST']))
        @method($method)
    @endif

    {{ $slot }}
</form>