themesberg / flowbite

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

Js components doesn't work with nuxt #57

Closed salvatorecalo closed 2 years ago

salvatorecalo commented 2 years ago

The js part of this library doesn't work with nuxt framework (Vuejs). I tried to include the import both in my AppBar.vue and in the main.js app but it doesn't work. I followed all the steps to download and include the library i my project, but while css part has been working, the js part doesn't and DOESN'T RETURN ANY ERROR IN CONSOLE My repo: https://github.com/salvatorecalo/salvatorecalo.github.io/tree/FlowBiteIssues

zoltanszogyenyi commented 2 years ago

Hey @salvatorecalo,

It seems that this problem is associated with statically generated websites that are based on virtual DOM's like NextJS and Nuxt. We'll take a good look into this next week and see if we can provide support before we finish the standalone React & Vue.js libraries which will work seamlessly with these technologies.

The reason the interactive elements don't work is that the time at which Flowbite JS is being included, the DOM probably doesn't exist yet because the querySelector's are not targeting the elements correctly.

This has been solved for simple React and Vue.js projects by applying a DOMContentLoaded event listener on the document object, but I'm not sure why these querySelectors are not finding the elements in Nuxt or NextJs.

salvatorecalo commented 2 years ago

Ok, Tomorrow I'll try!

oxynad commented 2 years ago

I believe I have the same issue with the Datepicker not working in my NextJS project.

gerasimovs commented 2 years ago

I wrote the code for dynamic DOM elements, please do not judge strictly - I'm a backend developer. Hope it solves your problem

import { createPopper } from '@popperjs/core';

document.addEventListener('DOMContentLoaded', () => {
    const triggers = [
        {event: 'mouseover', checker: isHoverable, fn: show},
        {event: 'mouseout', checker: isHoverable, fn: hide},
        {event: 'click', checker: isClickable, fn: show},
        {event: 'focusout', checker: isClickable, fn: hide},
        {event: 'focus', checker: () => true, fn: show},
        {event: 'blur', checker: () => true, fn: hide},
    ];

    triggers.forEach(({event, checker, fn}) => {
        document.addEventListener(event, e => {
            const tooltipToggleEl = e.target.closest('[data-tooltip-target]')

            if (tooltipToggleEl && checker(tooltipToggleEl)) {
                runTooltip(tooltipToggleEl, fn);
            }
        }, false);
    })

    function runTooltip(tooltipToggleEl, fn) {
        const tooltipEl = document.getElementById(tooltipToggleEl.getAttribute('data-tooltip-target'));
        const placement = tooltipToggleEl.getAttribute('data-tooltip-placement');

        const popperInstance = createPopper(tooltipToggleEl, tooltipEl, {
            placement: placement ? placement : 'top',
            modifiers: [
                {
                    name: 'offset',
                    options: {
                        offset: [0, 8],
                    },
                },
            ],
        });

        fn(tooltipEl, popperInstance);
    }

    function show(tooltipEl, popperInstance) {
        // Make the tooltip visible
        tooltipEl.classList.remove('opacity-0');
        tooltipEl.classList.add('opacity-100');
        tooltipEl.classList.remove('invisible');
        tooltipEl.classList.add('visible');

        // Enable the event listeners
        popperInstance.setOptions((options) => ({
            ...options,
            modifiers: [
                ...options.modifiers,
                { name: 'eventListeners', enabled: true },
            ],
        }));

        // Update its position
        popperInstance.update();
    }

    function hide(tooltipEl, popperInstance) {
        // Hide the tooltip
        tooltipEl.classList.remove('opacity-100');
        tooltipEl.classList.add('opacity-0');
        tooltipEl.classList.remove('visible');
        tooltipEl.classList.add('invisible');

        // Disable the event listeners
        popperInstance.setOptions((options) => ({
            ...options,
            modifiers: [
                ...options.modifiers,
                { name: 'eventListeners', enabled: false },
            ],
        }));
    }

    function isHoverable(tooltipToggleEl) {
        const trigger = tooltipToggleEl.getAttribute('data-tooltip-trigger');
        return !trigger || trigger === 'hover';
    }

    function isClickable(tooltipToggleEl) {
        return tooltipToggleEl.getAttribute('data-tooltip-trigger') === 'click';
    }
});
zoltanszogyenyi commented 2 years ago

Hey @oxynad @salvatorecalo @gerasimovs,

Please check out the Flowbite Vue component library for Vue or Nuxt projects.