livewire / flux

The official Livewire UI component library
https://fluxui.dev
461 stars 38 forks source link

Modal inside a tab freezes everything #556

Closed Hesesses closed 1 hour ago

Hesesses commented 3 days ago

I'm trying to add modal confirmation.

Sometimes when you click delete, the modal is shown but nothing works anymore (you cant click cancel or delete project, hover doesnt work) Sometimes cancel and delete project buttons works on modal, but when you click one of those, the modal is closed but everything has stopped working, no hovers, click buttons doesnt work, cant change tab etc. No console errors.

Everything works when code related to modal is removed.

Code:

<div class="space-y-6">
    <flux:tab.group>
        <flux:tabs wire:model="trans" wire:click="changeTrans" class="overflow-x-auto">
            @foreach (['en','fi','es'] as $language)
                <flux:tab name="{{ $language }}">
                    {{ $language }}
                </flux:tab>
            @endforeach

        </flux:tabs>

        @foreach (['en','fi','es'] as $translation)
            <flux:tab.panel :name="$translation">

                <form wire:submit="saveTranslation" class="space-y-6">

                    <div class="flex justify-between">
                        <div class="flex space-x-2">
                            <div>
                                <flux:button type="submit" variant="primary">{{ __('common.SAVE') }}</flux:button>
                            </div>
                        </div>

                        <div>
                            <flux:modal.trigger name="delete-profile">
                                <flux:button variant="danger">Delete</flux:button>
                            </flux:modal.trigger>

                            <flux:modal name="delete-profile" class="min-w-[22rem] space-y-6">
                                <div>
                                    <flux:heading size="lg">Delete project?</flux:heading>

                                    <flux:subheading>
                                        <p>You're about to delete this project.</p>
                                        <p>This action cannot be reversed.</p>
                                    </flux:subheading>
                                </div>

                                <div class="flex gap-2">
                                    <flux:spacer/>

                                    <flux:modal.close>
                                        <flux:button variant="ghost">Cancel</flux:button>
                                    </flux:modal.close>

                                    <flux:button wire:click="deleteTranslation()" variant="danger">Delete project
                                    </flux:button>
                                </div>
                            </flux:modal>
                        </div>
                    </div>

                </form>

            </flux:tab.panel>
        @endforeach

    </flux:tab.group>
</div>

Image

jeffchown commented 3 days ago

@Hesesses Are you able to provide code that doesn't have any dependencies so people can cut & paste to reproduce and examine the issue?

Hesesses commented 3 days ago

@Hesesses Are you able to provide code that doesn't have any dependencies so people can cut & paste to reproduce and examine the issue?

Updated!

lotje-kinable commented 3 days ago

@Hesesses did you try adding wire:key (to the foreach)?

Hesesses commented 3 days ago

Yes, Didnt help

jeffchown commented 3 days ago

@Hesesses Are you able to provide code that doesn't have any dependencies so people can cut & paste to reproduce and examine the issue?

Updated!

Great, @Hesesses .

Based on how the page appeared when inspecting the different elements using the dev tools, I think the issue has something to do with the flux:modal being contained in a flux:tab.panel.

I did two things to get it to work, I gave each modal a unique name and I used @teleport to move the <flux:modal> code outside of the <flux:tab.panel>:

    <div class="space-y-6">
        <flux:tab.group>
            <flux:tabs wire:model="trans" wire:click="changeTrans" class="overflow-x-auto">
                @foreach (['en','fi','es'] as $language)
                    <flux:tab name="{{ $language }}">
                        {{ $language }}
                    </flux:tab>
                @endforeach

            </flux:tabs>

            @foreach (['en','fi','es'] as $translation)
                <flux:tab.panel :name="$translation">

                    <form wire:submit="saveTranslation" class="space-y-6">

                        <div class="flex justify-between">
                            <div class="flex space-x-2">
                                <div>
                                    <flux:button type="submit" variant="primary">{{ __('common.SAVE') }}</flux:button>
                                </div>
                            </div>

                            <div>
                                <flux:modal.trigger :name='"delete-profile-$translation"'>
                                    <flux:button variant="danger">Delete</flux:button>
                                </flux:modal.trigger>

                                @teleport('body')
                                    <flux:modal :name='"delete-profile-$translation"' class="min-w-[22rem] space-y-6">
                                        <div>
                                            <flux:heading size="lg">Delete project?</flux:heading>

                                            <flux:subheading>
                                                <p>You're about to delete this project.</p>
                                                <p>This action cannot be reversed.</p>
                                            </flux:subheading>
                                        </div>

                                        <div class="flex gap-2">
                                            <flux:spacer/>

                                            <flux:modal.close>
                                                <flux:button variant="ghost">Cancel</flux:button>
                                            </flux:modal.close>

                                            <flux:button wire:click="deleteTranslation()" variant="danger">Delete project
                                            </flux:button>
                                        </div>
                                    </flux:modal>
                                @endteleport
                            </div>
                        </div>

                    </form>

                </flux:tab.panel>
            @endforeach

        </flux:tab.group>
    </div>
Hesesses commented 3 days ago

Thanks @jeffchown!

Is there going to be any "fixes" to be able to do it how I was trying to do it or is this the way to handle modals inside tabs?

jeffchown commented 3 days ago

You're welcome, @Hesesses !

I don't know re: any 'fixes'. I'm not part of the Flux team so you'll have to ask @calebporzio or @joshhanley.

That said, I believe the nature of a tab's panel's content being hidden/shown makes it difficult for the modal code to be properly implemented in the DOM.

cstisa commented 3 days ago

Hi,

I encountered some problems too, trying to add modal in dropdowns on each row of my table, and the @teleport did the trick!

Thanks @jeffchown for this tip!

jeffchown commented 3 days ago

@cstisa , @Hesesses FYI, A pattern I often use to avoid creating multiple modals for the same purpose, is:

<flux:button
    x-on:click.prevent="$dispatch('open-delete-profile-modal', { id: '{{ $profile->id }}' })"
    type="button"
    variant="ghost"
    icon="trash"
>
    Delete Profile
</flux:button>

This way, I only need one modal (it becomes 'reusable' by each of my table's rows), I know the modal markup code is positioned 'out of the way' near the bottom closing </body> tag and I have reduced the overall size and load of my page - all without the need to @teleport multiple sections of my Blade markup.

calebporzio commented 1 hour ago

The problem is that you are creating multiple modals of the same name because of the foreach loop. Because of this, triggering one modal is triggering them all and causing weird issues in the browser.

The fix is to "scope" the name of the modal inside the loop like so:

<flux:modal.trigger :name="'delete-profile-'.$translation">
    <flux:button variant="danger">Delete</flux:button>
</flux:modal.trigger>

<flux:modal :name="'delete-profile-'.$translation" class="min-w-[22rem] space-y-6">
    ...

I just added a new section to the modal docs to make this clear for folks: https://fluxui.dev/components/modal#unique-modal-names

Thanks!