neilberkman

neilberkman

I’m trying to implement some TailwindUI components using LiveView and some of these specify enter / leave transitions to be implemented in JS. As an example, if you go here and view the code you’ll see comments such as:

<!--
  'Solutions' flyout menu, show/hide based on flyout menu state.

  Entering: "transition ease-out duration-200"
    From: "opacity-0 translate-y-1"
    To: "opacity-100 translate-y-0"
  Leaving: "transition ease-in duration-150"
    From: "opacity-100 translate-y-0"
    To: "opacity-0 translate-y-1"
-->

(some background on why this can’t be done with CSS alone can be found here)

React, Vue and other frameworks have built-in support for such transitions, for example: Transition | Vue.js but it doesn’t appear that LiveView currently does, and I haven’t found an issue about this.

I was wondering if anyone has found a way to implement enter / leave transitions with LiveView. Thanks.

Showing Posts 1 to 10

Qqwy

Qqwy

TypeCheck Core Team

Do you mean transitions onmouseenter/onmouseout?

If so, these are not currently supported in LiveView yet. I hope this will change, c.f. Phoenix LiveView request/proposal: mouse events

neilberkman

neilberkman OP

Thanks, but the issue is also about the ability to change a property once the transition has completed. Here’s some further illustration from https://sebastiandedeyne.com/javascript-framework-diet/enter-leave-transitions/ :

Broadly speaking, there are two ways to implement enter & leave transitions:

  • Just use the transition CSS properties
  • Animations with JavaScript (either by modifying CSS properties, with the web animation API, or with third-party animation libraries)

Unfortunately, transition doesn’t cut it. Here’s what we need for our dropdown list transition:

  • When the dropdown opens, set it’s display property to block , then set transition opacity from 0 to 1
  • When the dropdown closes, transition opacity from 1 to 0 , then set it’s display property to none

We can’t build this with CSS transitions alone, because there’s no way to change the display value before or after the transition happened. I don’t like JavaScript animations for these things in JavaScript either, because it couples your JavaScript to CSS and vice-versa.

This is the scenario supported explicitly by Vue, React and other frameworks.

Actually I just noticed that you asked a similar question here: LiveView: Are there any clever techniques to do 'removal' animations?

why

why

I would also love to figure out how to do that without getting into Liveview’s hair :slightly_smiling_face:

My initial hope was to be able to use Alpine.js, as outlined here, but Alpine and Liveview appear to be incompatible as discussed here.

Have bumped into the same article you mentioned also and been studying it over the last couple days to see if this might be the way forward. But I’m not very experienced with javascript, and it’s a bit slow going :slight_smile:

Hoping to get it to work in conjunction with Liveview’s phx-update="ignore"

outlog

outlog

doesn’t css keyframes work for these multi step animations? - not sure I agree with the article but I’m certainly no css/js expert at all..

you should be able to adapt below code to show/hide that nav thing in tailwind..

Shared with Zight

if you take the phoenix image example:

add this above the form in the render in the component:
<p class="my-class <%= @bg %>">updated</p><br><br>

then the css:

.my-class.blue
{
    animation: fadeInFromNone 2.5s ease-out;
}
.my-class.black
{
    animation-fill-mode: forwards !important;
    animation: fadeOut 2.5s ease-out;
}

@keyframes fadeInFromNone {
    0% {
        display: none;
        opacity: 0;
    }

    1% {
        display: block;
        opacity: 0;
    }

    100% {
        display: block;
        opacity: 1;
    }
}

@keyframes fadeOut {
    0% {
        opacity: 1;
    }

    99% {
        opacity: 0;
    }

    100% {
        display: none;
        opacity: 0;
    }
}
outlog

outlog

had some spare time:
add tailwindcss and ui to your app
copy the html from Tailwind CSS Headers - Official Tailwind UI Components
put a phx-click on the more button, and add my_more_nav <%= @more_status %> to the div class

        <div class="relative">
          <!-- Item active: "text-gray-900", Item inactive: "text-gray-500" -->
          <button  phx-click="toggle_more" type="button" class="text-gray-500 inline-flex items-center space-x-2 text-base leading-6 font-medium hover:text-gray-900 focus:outline-none focus:text-gray-900 transition ease-in-out duration-150">
            <span>More</span>
            <!-- Item active: "text-gray-600", Item inactive: "text-gray-400" -->
            <svg class="text-gray-400 h-5 w-5 group-hover:text-gray-500 group-focus:text-gray-500 transition ease-in-out duration-150" fill="currentColor" viewBox="0 0 20 20">
              <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
            </svg>
          </button>

          <!--
            'More' flyout menu, show/hide based on flyout menu state.

            Entering: "transition ease-out duration-200"
              From: "opacity-0 translate-y-1"
              To: "opacity-100 translate-y-0"
            Leaving: "transition ease-in duration-150"
              From: "opacity-100 translate-y-0"
              To: "opacity-0 translate-y-1"
          -->
          <div class="my_more_nav <%= @more_status %> absolute left-1/2 transform -translate-x-1/2 mt-3 px-2 w-screen max-w-md sm:px-0">

css is: (probably some unneeded !important and what not)

div.my_more_nav:not(.show_more):not(.hide_more) {
  display: none;
}

div.show_more
{
    animation-fill-mode: forwards !important;
    animation: fadeInFromNone 0.2s ease-out;
}



div.hide_more
{ 

    animation-fill-mode: forwards !important;
    animation: fadeOut 0.15s ease-in;
}

@keyframes fadeInFromNone {
    0% {
        display: none;
        opacity: 0;
       transform: translateY(4px) translateX(-50%);
        
    }

    1% {
        display: block;
        opacity: 0;
        transform: translateY(4px) translateX(-50%);
    }

    100% {
        display: block !important;
        opacity: 1;
       transform: translateY(0px) translateX(-50%);
    }
}

@keyframes fadeOut {
    0% {
        display: block;
        opacity: 1;
        transform: translateY(0px) translateX(-50%);
    }

    99% {
        opacity: 0;
        transform: translateY(4px) translateX(-50%);
    }

    100% {
        display: none;
        opacity: 0;
        transform: translateY(4px) translateX(-50%);
    }
}

then in the liveview mount assign more_status: "" on the socket..

and have a toggle_more:

  @impl true
  def handle_event("toggle_more", _query, socket) do

   value = if socket.assigns.more_status == "show_more" do
       "hide_more"
     else
      "show_more"
   end
   {:noreply,
         socket
         |> assign(results: %{}, more_status: value)}
  end

most likely I would handle navbar clicks client side (do a phx-hook and addeventlistener on click) - I would also underlay the entire div - and catch clicks outside the navbar thing..

neilberkman

neilberkman OP

@outlog thanks so much! I’ll give it a try.

idi527

idi527

:waving_hand:

I think doing it the same way as it is done in alpine.js would work fine in live view: https://github.com/alpinejs/alpine/blob/master/src/utils.js#L132-L379

why

why

@outlog: Thanks a ton, this looks great! Working through it now…

@idi527: Thanks also. I have looked at the way alpine.js does it, but it’s a bit over my head in terms of js :blush:

why

why

Just to help me understand: Did you mean that you would have a full screen underlay (transparent), so that if somebody clicked outside of the open menu (navbar element), you’d be able to close it again?

Edit: Oops, meant to reply to @outlog, apologies…

outlog

outlog

exactly - all depends on your UI/UX preferences of course..

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
jaybe78
Hello, I’m developing a online persistent chat system (what’s app) like using elixir/dynamodb/aws for a mobile app(flutter). The diffic...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New
aseigo
ICal is a library for interacting with iCalendar data. It parses iCalendars into typed Elixir structs via ICal.from_ics, and can prepare ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews