themesberg / flowbite

Open-source UI component library and front-end development framework based on Tailwind CSS
https://flowbite.com
MIT License
8k stars 747 forks source link

Modals are unresponsive and lack backdrop in Rails 7.1.3 with Flowbite 2.3.0 #912

Open Duartemartins opened 5 months ago

Duartemartins commented 5 months ago

Description In a Rails 7 application using Flowbite 2.3.0, modals appear on the page but are unresponsive—clicks on modal buttons do not trigger any actions. Additionally, the modal lacks a visible backdrop, which affects the UI's focus and usability.

Hamburger menu works fine.

To Reproduce Steps to reproduce the behavior:

  1. Set up a Rails 7 application with Flowbite 2.3.0 integrated.
  2. Add a modal component to a view using Flowbite's standard modal configuration.
  3. Trigger the modal using a button or a link.
  4. Observe that the modal appears but buttons are unresponsive and no backdrop is visible.

Expected behavior I expected the modal to not only display but also respond to interactions (e.g., closing the modal or clicking any buttons within it). Additionally, a backdrop should appear behind the modal, dimming the rest of the web page to focus user interaction on the modal content.

Screenshots Here's a screengrab.

Desktop:

Smartphone :

Additional context This issue might be related to JavaScript or CSS conflicts within the Flowbite and Rails asset pipeline integration, possibly due to the Turbolinks/Turbo Drive in Rails. Any insights or fixes would be greatly appreciated.


importmap.rb:

pin '@hotwired/turbo-rails', to: 'turbo.min.js', preload: true
pin '@hotwired/stimulus', to: 'stimulus.min.js', preload: true
pin '@hotwired/stimulus-loading', to: 'stimulus-loading.js', preload: true
pin_all_from 'app/javascript/controllers', under: 'controllers'
pin "flowbite", to: "https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.turbo.min.js", preload: true
pin 'apexcharts', to: 'https://cdn.jsdelivr.net/npm/apexcharts@latest/dist/apexcharts.min.js', preload: true

application.js:

import "controllers"
import "flowbite"
import 'apexcharts'

tailwind-config.js:

module.exports = {
  content: [
    './public/*.html',
    './app/helpers/**/*.rb',
    './app/javascript/**/*.js',
    './app/views/**/*.{erb,haml,html,slim}'
  ],
  darkMode: 'class',
  theme: {
    extend: {
      colors: {
        primary: {"50":"#eff6ff","100":"#dbeafe","200":"#bfdbfe","300":"#93c5fd","400":"#60a5fa","500":"#3b82f6","600":"#2563eb","700":"#1d4ed8","800":"#1e40af","900":"#1e3a8a","950":"#172554"}
      }
    },
    fontFamily: {
      'body': [
    'Inter', 
    'ui-sans-serif', 
    'system-ui', 
    '-apple-system', 
    'system-ui', 
    'Segoe UI', 
    'Roboto', 
    'Helvetica Neue', 
    'Arial', 
    'Noto Sans', 
    'sans-serif', 
    'Apple Color Emoji', 
    'Segoe UI Emoji', 
    'Segoe UI Symbol', 
    'Noto Color Emoji'
  ],
      'sans': [
    'Inter', 
    'ui-sans-serif', 
    'system-ui', 
    '-apple-system', 
    'system-ui', 
    'Segoe UI', 
    'Roboto', 
    'Helvetica Neue', 
    'Arial', 
    'Noto Sans', 
    'sans-serif', 
    'Apple Color Emoji', 
    'Segoe UI Emoji', 
    'Segoe UI Symbol', 
    'Noto Color Emoji'
  ]
    }
  }
}
Duartemartins commented 5 months ago

This is caused by the backdrop div generated by flowbite. I cannot access the Modal class to generate the backdrop with different options.

Extremely hacky workaround for the time being:

// app/javascript/custom/modal.js
// Function to remove the modal backdrop div if it matches the criteria
document.addEventListener('turbo:load', () => {
    function removeModalBackdrop() {
        const backdrop = document.querySelector('div[modal-backdrop][class*="bg-gray-900"]');
        if (backdrop) {
            try {
                backdrop.remove();
                console.log('Modal backdrop removed.');
            } catch (error) {
                console.error('Error removing backdrop:', error);
            }
        } else {
            console.log('No backdrop found to remove.');
        }
    }
    // Create a new MutationObserver instance to monitor DOM changes
    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            if (mutation.addedNodes.length) {
                mutation.addedNodes.forEach(node => {
                    // Check if the added node is the backdrop or if the subtree contains it
                    if (node.nodeType === Node.ELEMENT_NODE && (node.matches('div[modal-backdrop][class*="bg-gray-900"]') || node.querySelector('div[modal-backdrop][class*="bg-gray-900"]'))) {
                        removeModalBackdrop(); // Attempt to remove the backdrop if it's added
                    }
                });
            }
        });
    });
    // Configuration for the observer: watch for the addition of child nodes and changes in the subtree
    const config = { childList: true, subtree: true };

    // Start observing the entire body of the document for changes
    observer.observe(document.body, config);

    // Perform an initial check in case the backdrop is already in the DOM when the script runs
    removeModalBackdrop();
});

importmap.rb: pin_all_from "app/javascript/custom", under: "custom"

zoltanszogyenyi commented 4 months ago

Hey @Duartemartins,

Can you please try with v2.4.1 too?

Cheers, Zoltan

Duartemartins commented 4 months ago

So now what happens is that a backdrop is created but on top of the modal, despite the z-index being lower. I was unable to modify it - when I changed the modal backdrop settings using the API a second backdrop would still be created on top of everything. The only thing I've found that makes the modals work is removing the backdrop altogether like so:

// application.js

initFlowbite();

// Function to destroy the backdrop
const destroyBackdrop = (modalId) => {
    const modalInstance = FlowbiteInstances.getInstance('Modal', modalId);
    if (modalInstance) {
        // Find the backdrop element
        const backdropElement = document.querySelector('.bg-gray-900\\/50.dark\\:bg-gray-900\\/80.fixed.inset-0.z-40');
        if (backdropElement) {
            backdropElement.remove();
        }
    }
};

// Event listener for modal buttons
document.addEventListener('click', function (event) {
    if (event.target.matches('[data-modal-toggle]')) {
        // Get the modal ID from the button's data attribute
        const modalId = event.target.getAttribute('data-modal-toggle');
        destroyBackdrop(modalId);
    }
});
blastedcode commented 3 weeks ago

Not sure if this helps, my issue was slightly different so I created a new issue. I have also managed to create a workaround. The solution might be useful

https://github.com/themesberg/flowbite/issues/989