@tsommie
1 month ago
448
3
General

Unable to swap Jetstream default TailwindCSS modal for Bootstrap Modal and still achieve the expected or same result as its was with TailwindCSS

Posted 1 month ago by @tsommie

Am working on a Laravel 8 package that swaps the #TALL stack for what I call the #BALL stack, and its basically a Bootstrap, AlpineJs Laravel, Livewire stack. Bootstrap 5 is leaning towards utility classes and no longer makes use of JQuery which gives room for AlpineJs so I really don't see a lot of upsides with Tailwind, not to mention I've gotten really comfortable with Bootstrap which should be all that matters at the end right?

Now the problem is this, I've been able to make changes to a lot of the JetStream blade files and if you've installed and made use of Jetstream then your familiar with this image:

enter image description here

(Yes!!!! Its the exact same thing but with Bootstrap!)

but one particular component has kept me up for days and its the Bootstrap Modal.

  1. Whenever I call the Bootstrap modal Livewire requests seem to make the Modal itself disappear leaving just the backdrop, forcing a page request before anything else can be clicked.

  2. The final request is never completed

  3. I have no way to hold the modal until a confirmation request is loaded and then programmatically disable the modal when finished.

The exact place I have this problem is in the views/api/api-token-manager.blade which is added by Jetstream when you install the livewire stack. My files looks like so:

My views/api/api-token-manager.blade

<!-- API Token List -->
// Inside a x-jet-action-section component
<x-slot name="content">
    <div class="space-y-6">
    @foreach ($this->user->tokens->sortBy('name') as $token)
        <div class="d-flex justify-content-between">
            <div>
                {{ $token->name }}
            </div>

            <div class="d-flex">
            @if ($token->last_used_at)
                <div class="text-sm text-muted">
                    Last used {{ $token->last_used_at->diffForHumans() }}
                </div>
            @endif

            @if (Laravel\Jetstream\Jetstream::hasPermissions())
                <button class="btn btn-link text-secondary" data-toggle="modal"
                                                wire:click="manageApiTokenPermissions({{ $token->id }})"
                                                data-target="#managingApiTokenPermissions-{{ $token->id }}">
                   Permissions
               </button>
            @endif

            <button class="btn btn-link text-danger text-decoration-none"
                                            data-toggle="modal"
                                            data-target="#confirmApiTokenDeletion-{{ $token->id }}">
                Delete
            </button>
            </div>
        </div>

                            <!-- API Token Permissions Modal -->
                            <x-jet-dialog-modal id="managingApiTokenPermissions-{{ $token->id }}">
                                <x-slot name="title">
                                    API Token Permissions
                                </x-slot>

                                <x-slot name="content">
                                    <div class="mt-2 row">
                                        @foreach (Laravel\Jetstream\Jetstream::$permissions as $permission)
                                            <div class="col-6">
                                                <div class="form-check">
                                                    <input class="form-check-input" type="checkbox" value="{{ $permission }}" wire:model.defer="updateApiTokenForm.permissions">
                                                    <label class="form-check-label">
                                                        {{ $permission }}
                                                    </label>
                                                </div>
                                            </div>
                                        @endforeach
                                    </div>
                                </x-slot>

                                <x-slot name="footer">
                                    <x-jet-secondary-button data-dismiss="modal">
                                        {{ __('Nevermind') }}
                                    </x-jet-secondary-button>

                                    <x-jet-button class="ml-2" wire:click="updateApiToken"
                                                  wire:click="$emit('updateApiToken', {{ $token->id }})"
                                                  data-dismiss="modal">
                                        {{ __('Save') }}
                                    </x-jet-button>
                                </x-slot>
                            </x-jet-dialog-modal>
                            @push('scripts')
                                <script>
                                    Livewire.on('updateApiToken', id => {
                                        @this.manageApiTokenPermissions(id)
                                        @this.updateApiToken()
                                    })
                                </script>
                            @endpush

                            <!-- Delete Token Confirmation Modal -->
                            <x-jet-confirmation-modal id="confirmApiTokenDeletion-{{ $token->id }}">
                                <x-slot name="title">
                                    Delete API Token
                                </x-slot>

                                <x-slot name="content">
                                    Are you sure you would like to delete this API token?
                                </x-slot>

                                <x-slot name="footer">
                                    <x-jet-secondary-button data-dismiss="modal">
                                        Nevermind
                                    </x-jet-secondary-button>

                                    <x-jet-danger-button class="ml-2"
                                                         wire:click="$emit('deleteApiToken', {{ $token->id }})"
                                                         data-dismiss="modal">
                                        Delete
                                    </x-jet-danger-button>
                                </x-slot>
                            </x-jet-confirmation-modal>
                            @push('scripts')
                                <script>
                                    Livewire.on('deleteApiToken', id => {
                                        @this.confirmApiTokenDeletion(id)
                                        @this.deleteApiToken()
                                    })
                                </script>
                            @endpush
                        @endforeach
                    </div>
                </x-slot>

My x-jet-dialog-modal component

@props(['id' => null, 'maxWidth' => null])

<x-jet-modal :id="$id" :maxWidth="$maxWidth" {{ $attributes }}>
    <div class="modal-content">
        <div class="modal-header">
            <h5 class="modal-title">{{ $title }}</h5>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                <span aria-hidden="true">&times;</span>
            </button>
        </div>
        <div class="modal-body">
            {{ $content }}
        </div>
        <div class="modal-footer">
            {{ $footer }}
        </div>
    </div>
</x-jet-modal>

My x-jet-modal component

@props(['id', 'maxWidth', 'modal' => false])

@php
$id = $id ?? md5($attributes->wire('model'));

switch ($maxWidth ?? '') {
    case 'sm':
        $maxWidth = ' modal-sm';
        break;
    case 'md':
        $maxWidth = '';
        break;
    case 'lg':
        $maxWidth = ' modal-lg';
        break;
    case 'xl':
        $maxWidth = ' modal-xl';
        break;
    case '2xl':
    default:
        $maxWidth = '';
        break;
}
@endphp

<!-- Modal -->
<div class="modal fade" tabindex="-1" id="{{ $id }}" aria-labelledby="{{ $id }}" aria-hidden="true">
    <div class="modal-dialog{{ $maxWidth }}">
        {{ $slot }}
    </div>
</div>

I really don't want to make the change to Tailwind regardless of the hype so I would love any assistance the community can render.

To make a direct contribution to the project click here.

Thanks!!!

Please sign in or create an account to participate in this conversation.