Overlays

dropdown-menu

Click-triggered menu with outside-click and Esc to close.

<x-ui.dropdown-menu>
    <x-slot:trigger>
        <x-ui.button variant="outline">Open menu</x-ui.button>
    </x-slot:trigger>

    <button role="menuitem"
        class="block w-full rounded-sm px-3 py-1.5 text-left text-sm hover:bg-accent hover:text-accent-foreground">
        Profile
    </button>
    <button role="menuitem"
        class="block w-full rounded-sm px-3 py-1.5 text-left text-sm hover:bg-accent hover:text-accent-foreground">
        Settings
    </button>
    <x-ui.separator class="my-1" />
    <button role="menuitem"
        class="block w-full rounded-sm px-3 py-1.5 text-left text-sm text-destructive hover:bg-accent">
        Sign out
    </button>
</x-ui.dropdown-menu>

Installation

php artisan ui:add dropdown-menu

1. Install dependencies

  • composer: gehrisandro/tailwind-merge-laravel
  • npm: alpinejs

2. Copy the source into resources/views/components/ui/

resources/views/components/ui/dropdown-menu.blade.php

@props([
    'align' => 'start',
])

@php
    $alignment = match ($align) {
        'end' => 'right-0 origin-top-right',
        'center' => 'left-1/2 -translate-x-1/2 origin-top',
        default => 'left-0 origin-top-left',
    };

    $menuClasses = \TailwindMerge\Laravel\Facades\TailwindMerge::merge(
        'absolute z-50 mt-2 min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
        $alignment,
        $attributes->get('class'),
    );
@endphp

<div class="relative inline-block text-left" x-data="{ open: false }"
    @keydown.escape.window="open = false" @click.outside="open = false">
    <div @click="open = !open" x-bind:aria-expanded="open.toString()"
        aria-haspopup="menu">
        {{ $trigger }}
    </div>

    <div x-show="open" x-cloak x-transition role="menu"
        class="{{ $menuClasses }}">
        {{ $slot }}
    </div>
</div>

Usage

<x-ui.dropdown-menu>
    <x-slot:trigger>
        <x-ui.button variant="outline">Open menu</x-ui.button>
    </x-slot:trigger>

    <button role="menuitem"
        class="block w-full rounded-sm px-3 py-1.5 text-left text-sm hover:bg-accent hover:text-accent-foreground">
        Profile
    </button>
</x-ui.dropdown-menu>

Requires Alpine.js. `trigger` slot opens it; put menu items (role=menuitem) in the default slot. align: start|center|end.