Skip to main content

Overlays

Popover code

Copy the implementation or inspect every file that belongs to this component unit.

resources/views/components/ui/popover.blade.php

@props([
    'align' => 'bottom',
    'width' => '72',
])

@php
    /*
    | Click-to-open anchored panel for rich content (unlike tooltip's plain text
    | or dropdown's menu rows). Put the trigger in the `trigger` slot and any
    | markup in the default slot.
    |
    | align: top, bottom, left, right
    |
    | Usage:
    |   <x-ui.popover>
    |       <x-slot:trigger><x-ui.button variant="outline">Details</x-ui.button></x-slot:trigger>
    |       <p>Any rich content here.</p>
    |   </x-ui.popover>
    */
    $alignClass = [
        'bottom' => 'top-full start-0 mt-2',
        'top' => 'bottom-full start-0 mb-2',
        'left' => 'end-full top-0 me-2',
        'right' => 'start-full top-0 ms-2',
    ][$align] ?? 'top-full start-0 mt-2';

    $widthClass = [
        '56' => 'w-56', '64' => 'w-64', '72' => 'w-72', '80' => 'w-80', '96' => 'w-96',
    ][$width] ?? $width;

    $popoverId = 'popover-'.substr(md5($align.$width.$slot), 0, 10);
@endphp

<div x-data="{ open: false }" @click.outside="open = false" @keydown.escape.window="open = false" {{ $attributes->merge(['class' => 'relative inline-block']) }}>
    <div
        @click="open = ! open"
        @keydown.enter.prevent="open = ! open"
        @keydown.space.prevent="open = ! open"
        :aria-expanded="open.toString()"
        aria-controls="{{ $popoverId }}"
        aria-haspopup="dialog"
    >
        {{ $trigger }}
    </div>

    <div
        id="{{ $popoverId }}"
        x-show="open" x-cloak
        x-transition:enter="transition ease-out duration-150"
        x-transition:enter-start="opacity-0 scale-95"
        x-transition:enter-end="opacity-100 scale-100"
        x-transition:leave="transition ease-in duration-100"
        x-transition:leave-start="opacity-100 scale-100"
        x-transition:leave-end="opacity-0 scale-95"
        role="dialog"
        class="absolute z-50 {{ $widthClass }} {{ $alignClass }} rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-4 text-sm text-gray-600 dark:text-gray-300 shadow-lg ring-1 ring-black/5"
    >
        {{ $slot }}
    </div>
</div>