blade-ui-kit / blade-ui-kit

A set of renderless components to utilise in your Laravel Blade views.
https://blade-ui-kit.com
MIT License
1.5k stars 92 forks source link

Dropdown Component Slots Not Working in Laravel Blade with Alpine.js #166

Open oussama166 opened 2 months ago

oussama166 commented 2 months ago

Dropdown Component Slots Not Working in Laravel Blade with Alpine.js

Summary

The dropdown component in Laravel Blade is not displaying its content correctly when using named slots for the trigger and dropdown content. The issue seems related to the interaction between Blade components and Alpine.js.

Steps to Reproduce

  1. Create a Dropdown Component:

    • resources/views/components/dropdown.blade.php
    @props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white dark:bg-gray-700'])
    
    @php
       $alignmentClasses = match ($align) {
           'left' => 'ltr:origin-top-left rtl:origin-top-right start-0',
           'top' => 'origin-top',
           default => 'ltr:origin-top-right rtl:origin-top-left end-0',
       };
    
       $widthClasses = match ($width) {
           '48' => 'w-48',
           default => $width,
       };
    @endphp
    
    <div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
       <div @click="open = !open" class="text-2xl">
           {{ $trigger }}
       </div>
    
       <div x-show="open" x-transition:enter="transition ease-out duration-200"
            x-transition:enter-start="opacity-0 scale-95" x-transition:enter-end="opacity-100 scale-100"
            x-transition:leave="transition ease-in duration-75" x-transition:leave-start="opacity-100 scale-100"
            x-transition:leave-end="opacity-0 scale-95"
            class="absolute z-50 mt-2 {{ $widthClasses }} rounded-md shadow-lg {{ $alignmentClasses }}">
           <div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}">
               {{ $slot }}
           </div>
       </div>
    </div>
  2. Use the Dropdown Component:

    • resources/views/layouts/navigation.blade.php
    <div class="hidden sm:flex sm:items-center sm:ms-6">
       <x-dropdown align="right" width="48">
           <x-slot name="trigger">
               <button class="inline-flex items-center px-3 py-2 text-sm font-medium leading-4 text-gray-500 transition duration-150 ease-in-out bg-white border border-transparent rounded-md dark:text-gray-400 dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none">
                   <div>{{ Auth::user()->first_name }}</div>
                   <div class="ms-1">
                       <svg class="w-4 h-4 fill-current" 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>
               </button>
           </x-slot>
    
           <x-slot>
               <x-dropdown-link :href="route('profile.edit')">
                   {{ __('Profile') }}
               </x-dropdown-link>
               <x-dropdown-link :href="route('profile.edit')">
                   {{ __('Settings') }}
               </x-dropdown-link>
    
               <!-- Authentication -->
               <form method="POST" action="{{ route('logout') }}">
                   @csrf
                   <x-dropdown-link :href="route('logout')" onclick="event.preventDefault(); this.closest('form').submit();">
                       {{ __('Log Out') }}
                   </x-dropdown-link>
               </form>
           </x-slot>
       </x-dropdown>
    </div>

Expected Behavior

Actual Behavior

Additional Information

Environment

Screenshots or Logs

image

alexmanase commented 2 months ago

Did you try to use the default slot without <x-slot></x-slot>?

https://laravel.com/docs/11.x/blade#slots

image
oussama166 commented 2 months ago

I will try it now thank you

oussama166 commented 2 months ago

@alexmanase Sorry but always the same issue :

image

alexmanase commented 2 months ago

Can you show me your code?

oussama166 commented 2 months ago
{{ config('app.name', 'Laravel') }} @vite(['resources/css/app.css', 'resources/js/app.js'])
@include('layouts.navigation') @isset($header)
{{ $header }}
@endisset
{{ $slot }}


- ressources/views/navigation.blade.php :
```php
<nav x-data="{ HumOpen: false }" class="bg-white border-b border-gray-100 dark:bg-gray-800 dark:border-gray-700">
    <!-- Primary Navigation Menu -->
    <div class="px-4 mx-auto max-w-7xl sm:px-6 lg:px-8">
        <div class="flex justify-between h-16">
            <div class="flex">
                <!-- Logo -->
                <div class="flex items-center shrink-0">
                    <a href="{{ route('dashboard') }}">
                        <x-application-logo class="block w-20 h-20 text-gray-800 fill-current dark:text-gray-200"/>
                    </a>
                </div>

                <!-- Navigation Links -->
                <div class="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex">
                    <x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
                        {{ __('Dashboard') }}
                    </x-nav-link>
                </div>
            </div>

            <!-- Settings Dropdown -->
            <div class="hidden sm:flex sm:items-center sm:ms-6">
                <x-dropdown align="right" width="48">
                    <x-slot name="trigger">
                        <button
                                class="inline-flex items-center px-3 py-2 text-sm font-medium leading-4 text-gray-500 transition duration-150 ease-in-out bg-white border border-transparent rounded-md dark:text-gray-400 dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none">

                            <div>{{ Auth::user()->first_name }} {{ Auth::user()->last_name }}</div>

                            <div class="ms-1">
                                <svg class="w-4 h-4 fill-current" 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>
                        </button>
                    </x-slot>

                    <x-slot name="content">
                        <x-dropdown-link :href="route('profile.edit')">
                            {{ __('Profile') }}
                        </x-dropdown-link>
                        <x-dropdown-link :href="route('profile.edit')">
                            {{ __('Settings') }}
                        </x-dropdown-link>

                        <!-- Authentication -->
                        <form method="POST" action="{{ route('logout') }}">
                            @csrf
                            <x-dropdown-link :href="route('logout')" onclick="event.preventDefault();
                                                this.closest('form').submit();">
                                {{ __('Log Out') }}
                            </x-dropdown-link>
                        </form>
                    </x-slot>

                </x-dropdown>
            </div>

            <!-- SM version -->
            <!-- Hamburger -->
            <div class="flex items-center -me-2 sm:hidden">
                <button @click="HumOpen = ! HumOpen"
                        class="inline-flex items-center justify-center p-2 text-gray-400 transition duration-150 ease-in-out rounded-md dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-900 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-500 dark:focus:text-gray-400">
                    <svg class="w-6 h-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
                        <path :class="{'hidden': HumOpen, 'inline-flex': ! HumOpen }" class="inline-flex"
                              stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                              d="M4 6h16M4 12h16M4 18h16"/>
                        <path :class="{'hidden': ! HumOpen, 'inline-flex': HumOpen }" class="hidden" stroke-linecap="round"
                              stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
                    </svg>
                </button>
            </div>
        </div>
    </div>

    <!-- Responsive Navigation Menu -->
    <div :class="{'block': HumOpen, 'hidden': ! HumOpen}" class="hidden sm:hidden">
        <div class="pt-2 pb-3 space-y-1">
            <x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
                {{ __('Dashboard') }}
            </x-responsive-nav-link>
            <x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('user_profile')">
                {{ __('User Profile') }}
            </x-responsive-nav-link>
        </div>

        <!-- Responsive Settings Options -->
        <div class="pt-4 pb-1 border-t border-gray-200 dark:border-gray-600">
            <div class="px-4">
                <div class="text-base font-medium text-gray-800 dark:text-gray-200">{{ Auth::user()->name }}</div>
                <div class="text-sm font-medium text-gray-500">{{ Auth::user()->email }}</div>
            </div>

            <div class="mt-3 space-y-1">
                <x-responsive-nav-link :href="route('profile.edit')">
                    {{ __('Profile') }}
                </x-responsive-nav-link>
                <x-responsive-nav-link :href="route('profile.edit')">
                    {{ __('Settings') }}
                </x-responsive-nav-link>

                <!-- Authentication -->
                <form method="POST" action="{{ route('logout') }}">
                    @csrf

                    <x-responsive-nav-link :href="route('logout')" onclick="event.preventDefault();
                                        this.closest('form').submit();">
                        {{ __('Log Out') }}
                    </x-responsive-nav-link>
                </form>
            </div>
        </div>
    </div>
</nav>

@php $alignmentClasses = match ($align) { 'left' => 'ltr:origin-top-left rtl:origin-top-right start-0', 'top' => 'origin-top', default => 'ltr:origin-top-right rtl:origin-top-left end-0', };

$width = match ($width) {
'48' => 'w-48',
default => $width,
};

@endphp

<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false"> <div @click="alert('hello');" class="text-2xl"> {{ $trigger }}

<div x-show="open" x-transition:enter="transition ease-out duration-200"
     x-transition:enter-start="opacity-0 scale-95" x-transition:enter-end="opacity-100 scale-100"
     x-transition:leave="transition ease-in duration-75" x-transition:leave-start="opacity-100 scale-100"
     x-transition:leave-end="opacity-0 scale-95"
     style="display: none;"
     class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}"
     @click="open = false">
    <div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}">
        {{ $content }}
    </div>
</div>

alexmanase commented 2 months ago

You have to use:

// navigation.blade.php
<x-slot name="content">
// dropdown main slot content
</x-slot>

Edit: fix typo

oussama166 commented 2 months ago

I will try it another time but already try it but she doesn't work but i will try it now

oussama166 commented 2 months ago

@alexmanase sorry sir you mean <x-slot name="content"> not <x-slot name"content">?

alexmanase commented 2 months ago

Yes, <x-slot name="content">. Sorry for the typo.

cyrussutaria commented 1 month ago

Same issue here. . Existing Navbar Dropdown stops working as soon as BUK is installed (i renamed old component to make sure it doesnt conflict) The trigger renders, but the content doesnt show up when trigger is clicked.

These are the default components that come with Breeze, I havent changed anything much.

navigation.blade.php

<!-- Settings Dropdown -->
            <div class="hidden sm:flex sm:items-center sm:ms-6">
                <x-dropdown align="right" width="48">
                    <x-slot name="trigger">
                        <button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
                            <div>{{ Auth::user()->name ?? "User"}}</div>

                            <div class="ms-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>
                        </button>
                    </x-slot>                    

                    <x-slot name="content">
                        <x-dropdown-link :href="route('profile.edit')">
                            {{ __('Profile') }}
                        </x-dropdown-link>

                        <!-- Authentication -->
                        <form method="POST" action="{{ route('logout') }}">
                            @csrf

                            <x-dropdown-link :href="route('logout')"
                                    onclick="event.preventDefault();
                                                this.closest('form').submit();">
                                {{ __('Log Out') }}
                            </x-dropdown-link>
                        </form>
                    </x-slot>
                </x-dropdown>

            </div>

No dropdown component cause I removed/renamed it

dropdown-link.blade.php

<a {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out']) }}>{{ $slot }}</a>

Any recommedations to resolve will be appreciated

driesvints commented 1 month ago

@cyrussutaria did you try prefixing the BUK components so they don't conflict? https://blade-ui-kit.com/docs/0.x/installation#prefixing

cyrussutaria commented 1 month ago

I had forgotted about that. Prefix helped me not having to rename individual components seperately. And i figured the issue as well. I was using the "trigger - content" model instead of "trigger - slot".

This works


<div x-data="{ open: false }" @click.outside="open = false" {{ $attributes }} @close.stop="open = false" > 
    <div @click="open = ! open">
        {{ $trigger }}
    </div>

    <div x-show="open">
        {{ $slot }}
    </div>
</div>

This doesnt (and it shouldn't ). I was reusing code from default laravel - Breeze scaffolding.


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

    <div x-show="open"            
            style="display: none;"
            @click="open = false">
        <div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}">
            {{ $content }}
        </div>
    </div>
</div>

Its resolved for me. Thanks, next time I'll RTFM.