How to use TailwinUI that needs JS in LiveView application?


I have app.html.heex with a top bar that I got from TailwinUI : Stacked Layouts - Official Tailwind CSS UI Components

However, I see in the notes and comments that I need JS for this:

                Dropdown menu, show/hide based on menu state.

                Entering: "transition ease-out duration-100"
                  From: "transform opacity-0 scale-95"
                  To: "transform opacity-100 scale-100"
                Leaving: "transition ease-in duration-75"
                  From: "transform opacity-100 scale-100"
                  To: "transform opacity-0 scale-95"
              <div class="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1">
                <!-- Active: "bg-gray-100", Not Active: "" -->
                <a href="#" class="block px-4 py-2 text-sm text-gray-700" role="menuitem" tabindex="-1" id="user-menu-item-0">Your Profile</a>
                <a href="#" class="block px-4 py-2 text-sm text-gray-700" role="menuitem" tabindex="-1" id="user-menu-item-2">Sign out</a>

I though I didn’t need to use JS directly on Phoenix 1.7.

  • Is there a way to have the menu drop and lift when clicked without using JS?
  • If I do have to use JS directly, where would I put it?

Have a look at the LiveView JS module.

Here’s an example where there is a show/hide modal.

def show_modal(js \\ %JS{}, id) when is_binary(id) do
      to: "##{id}",
      display: "inline-block",
      transition: {"ease-out duration-300", "opacity-0", "opacity-100"}
      to: "##{id}-container",
      display: "inline-block",
        {"ease-out duration-300", "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95",
         "opacity-100 translate-y-0 sm:scale-100"}
    |> js_exec("##{id}-confirm", "focus", [])

Edit: Also as to the “I thought I don’t need JS in Liveview”
While you can do a great deal without JS I would not recommend trying to avoid JS.
That said, the way you interface with JS is much different if you choose to use the Liveview JS module.
But by no means are you limited to just using the Liveview JS modules.
I would spend a little time reading this JavaScript interoperability — Phoenix LiveView v0.19.5


Given this code is in app.html.heex where would I put the show_hide_modal function? In which module?

I would read how they do it in that example I show for live beats.

They also have the modal in the main layout too.
They even did a good job describing the fact this was in the layout and they use send_update as a way to talk with said component.

Honestly just spend a good amount of time just reading and trying to understand how they got the modal to work and it will answer your question.

I apologize but reading code I can barely understand is not productive for me when learning a new technology.
This is mainly why I mostly focus on tutorials, guides and the community to ask guidance.

I understand you are not like me, I am happy for you. I would really appreciate if you could point me in another direction.

1 Like

My apologizes,
I realize I’m asking you to do the hard longer path.
I will only say it will be just that much more rewarding if you keep with it.
I believe that while it maybe hard to understand at first, I too took a long time and many re readings of this code before I understood it too.

I just would hate to think what you will be missing out on if you don’t try to push past the hard part.

And honestly try and get as far as you can, then come back with more questions. I will be waiting and willing to help and so are others here.

In other words you can’t rush this stuff. There is no easy ticket to learning this.

My best advice, read the Liveview documentation on top of using the LiveBeats as a starting point.
Or even just generate a basic scaffold app, it too has many great and closely resembling examples.

So all that said.
#1 you are trying to access a liveview component at the layout level.
You need to take this into consideration as most liveview components are children of a main liveview component. Thus the send_update function maybe a needed thing to learn.

#2 Look at how the LiveView JS module works. It has the transformation options you are looking for.

#3 Embrace vanilla JS and ES6 standards you will still need these and the more you learn the easier it will be to understand what benefits LiveView is giving you.


I see in the documentation examples we have JS.hide and, but I was not able to find a JS.toggle option or similar.

Given the sample:

<div id="item">My Item</div>

<button phx-click={JS.hide(to: "#item")}>

<button phx-click={JS.hide(to: "#item", transition: "fade-out-scale")}>
  hide fancy!

Will hide the item if it is visible and do nothing if its not (makes sense given the function’s name). Is there a function/way that I am missing that shows/hides the components?

I take from LiveBeats that when showing a dropdown I must add a class that tells me it is expanded:

  def show_dropdown(to) do
      to: to,
        {"transition ease-out duration-120", "transform opacity-0 scale-95",
         "transform opacity-100 scale-100"}
    |> JS.set_attribute({"aria-expanded", "true"}, to: to)

  def hide_dropdown(to) do
      to: to,
        {"transition ease-in duration-120", "transform opacity-100 scale-100",
         "transform opacity-0 scale-95"}
    |> JS.remove_attribute("aria-expanded", to: to)

But I wonder if there is a another way of doing it.

After further studying LiveBeats and docs, I was able to come up with something that works:

<div class="hidden" id="item">My Item</div>

<button phx-click={JS.hide(to: "#item")}>

<button phx-click={ "#item", transition: "fade-out-scale")} phx-click-away={JS.hide(to: "#item", transition: "fade-in-scale")}>

This will show the item when the button is clicked and hide it when you click away. If you click the button while the item is visible, nothing happens.

I understand that if I want more custom behaviour I can add it in components/core_components like in the Project @polygonpusher suggested.

1 Like

1 Like