tailwindcss-stimulus-components icon indicating copy to clipboard operation
tailwindcss-stimulus-components copied to clipboard

Slideover is child to Overlay - which means overlay animations get applied to slideover

Open philipithomas opened this issue 4 years ago • 4 comments
trafficstars

I've been trying to get the Slideover controller to work. And, I've noticed that in the example and the docs - the "Overlay" is set to a div that is the parent of the "Menu". It seems that this is a mistake compared to the Tailwind docs - which apply the Overlay transitions to a nested element peer to the slideover.

The result is that the animations from Overlay are being applied to Slideover. You see this in the example site because the "slideover" gets the fade-in animation from the overlay applied to its entrance.

When I move the data-slideover-target="overlay" to the child element that matches the Tailwind docs, the parent menu item (which is an inset) blocks the dom because it's not hidden or unhidden.

I need to continue experimenting to find a solution - but I think it might require a data-slideover-target="sidebar" that shows/hides the entire sidebar independent of the overlay and slideover animations.

philipithomas avatar Nov 05 '21 22:11 philipithomas

I've been looking into this too, did you manage to find a workaround? Ideally the slideover would slide in and out rather than fading.

gjones avatar Nov 29 '21 17:11 gjones

I took a shortcut of having the overlay slide in with the sidebar, without a separate animation. It looks decent enough @gjones.

philipithomas avatar Nov 29 '21 17:11 philipithomas

I was just working on this same thing yesterday. @philipithomas can you share code on how you achieved the slideover for entering?

sethaddison avatar Nov 30 '21 17:11 sethaddison

I have found a suboptimal solution, I created my own DropdownController and SlideoverController. I had to change both as I could not get the entering transitions to work otherwise.

The code for getting the transitions to work came from https://github.com/mmccall10/el-transition.

This is what I ended up with:


// transition_helper.js

export async function transition(target, transitionClassList, startClassList, endClassList) {
    transitionClassList.forEach(klass => target.classList.add(klass))
    startClassList.forEach(klass => target.classList.add(klass))
    await nextFrame();
    startClassList.forEach(klass => target.classList.remove(klass))
    endClassList.forEach(klass => target.classList.add(klass))
    await afterTransition(target)
    endClassList.forEach(klass => target.classList.remove(klass))
    transitionClassList.forEach(klass => target.classList.remove(klass))
}

function nextFrame() {
    return new Promise(resolve => {
        requestAnimationFrame(() => {
            requestAnimationFrame(resolve)
        });
    });
}

function afterTransition(element) {
    return new Promise(resolve => {
        // safari return string with comma separate values
        const computedDuration = getComputedStyle(element).transitionDuration.split(",")[0]
        const duration = Number(computedDuration.replace('s', '')) * 1000;
        setTimeout(() => {
            resolve()
        }, duration)
    });
}
// custom_dropdown_controller.js
// other than the two methods below the code is the same as dropdown_controller.js

async _show(cb) {
        this.menuTarget.classList.remove(this.toggleClass)
        this.element.setAttribute("aria-expanded", "true")

        await transition(
            this.menuTarget,
            this._enteringClassList[0],
            this._invisibleClassList[0],
            this._visibleClassList[0]);

        if (typeof cb == 'function') {
            await cb()
        }
}

async _hide(cb) {
        await transition(
            this.menuTarget,
            this._leavingClassList[0],
            this._visibleClassList[0],
            this._invisibleClassList[0]);

        if (typeof cb == 'function') {
            await cb()
        }

        this.element.setAttribute("aria-expanded", "false")
        this.menuTarget.classList.add(this.toggleClass)
}
// custom_slideover_controller.js
// inheriting from custom_dropdown_controller.js

// added another target to solve the original issue in this thread
static targets = ['menu', 'overlay', 'sidebar']

async _show() {
        this.sidebarTarget.classList.remove(this.toggleClass)
        this.overlayTarget.classList.remove(this.toggleClass)

         const parent_show_promise = super._show()
         const transition_promise = transition(
                 this.overlayTarget,
                 this._enteringClassList[1],
                 this._invisibleClassList[1],
                 this._visibleClassList[1]);
         await Promise.all([parent_show_promise, transition_promise])
}

async _hide() {
        const parent_show_promise = super._hide()
        const transition_promise = await transition(
            this.overlayTarget,
            this._leavingClassList[1],
            this._visibleClassList[1],
            this._invisibleClassList[1]);

        await Promise.all([parent_show_promise, transition_promise]);

        this.overlayTarget.classList.add(this.toggleClass)
        this.sidebarTarget.classList.add(this.toggleClass)
}

coder17934 avatar Jan 10 '22 12:01 coder17934

I've switched slideovers to use the <dialog> element so they can be implemented like modals.

excid3 avatar Mar 22 '24 00:03 excid3