Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save secretpray/a1d3253124205f25808f2e09ae365c5a to your computer and use it in GitHub Desktop.
Save secretpray/a1d3253124205f25808f2e09ae365c5a to your computer and use it in GitHub Desktop.
Animate Tailwind dropdown in Rails 7 ( importmap)
  1. config/importmap.rb
# Pin npm packages by running ./bin/importmap

pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "https://ga.jspm.io/npm:@hotwired/stimulus@3.2.1/dist/stimulus.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin "stimulus-use", to: "https://ga.jspm.io/npm:stimulus-use@0.52.0/dist/index.js"
  1. navbar.html.erb
<div class='hidden lg:flex items-center gap-4'>
  <% if user_signed_in? %>
    <div class="relative ml-3" data-controller='dropdown'>
      <div class='flex gap-2 align-center'>
        <button type="button" 
                class="relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" 
                id="user-menu-button" 
                aria-expanded="false" 
                aria-haspopup="true"
                data-action='click->dropdown#toggle'>
          <span class="absolute -inset-1.5"></span>
          <span class="sr-only">Open user menu</span>
          <%= image_tag current_user.image, class: 'h-8 w-auto rounded-full', alt: current_user.email if current_user.image? %>
        </button>
        <div class='hover:text-rose-400 active:text-rose-300 focus: active:text-rose-300'>
          <span class='cursor-pointer' data-action='click->dropdown#toggle'>
            <%= username(current_user) %>
          </span>
        </div>
      </div>

      <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 hidden" 
           data-dropdown-target="content"
           data-transition-enter-active="transition ease-out duration-200"
           data-transition-enter-from="transform opacity-0 scale-95"
           data-transition-enter-to="transform opacity-100 scale-100"
           data-transition-leave-active="transition ease-in duration-75"
           data-transition-leave-from="transform opacity-100 scale-100"
           data-transition-leave-to="transform opacity-0 scale-95"
           role="menu" 
           aria-orientation="vertical" 
           aria-labelledby="user-menu-button" 
           tabindex="-1">
        <!-- Active: "bg-gray-100", Not Active: "" -->

        <%= if current_user.admin?
              link_to t('.admin'), 
                      '/admin', 
                      class: 'block px-4 mx-1 py-2 text-sm text-gray-700 hover:bg-sky-900 hover:text-gray-200 rounded-md',
                      role: :menuitem,
                      tabindex: '-1',
                      id: 'user-menu-item-0'
            end %>
        <%= link_to t('.dashboard'), 
                    dashboard_path, 
                    class: 'block px-4 mx-1 py-2 text-sm text-gray-700 hover:bg-sky-900 hover:text-gray-200 rounded-md',
                    role: :menuitem,
                    tabindex: '-1',
                    id: 'user-menu-item-0' %>
        <%= link_to t('.logout'),
                    sign_out_path,
                    data: { turbo_method: :delete },
                    class: 'block px-4 mx-1 py-2 text-sm text-gray-700 hover:bg-sky-900 hover:text-gray-200 rounded-md' %>
      </div>
    </div>
  <% else %>
    <%= render 'shared/signup_links' %>
  <% end %>
</div>
  1. Stimulus
import { Controller } from "@hotwired/stimulus"
import { useClickOutside, useTransition } from "stimulus-use"

// Connects to data-controller="dropdown"
export default class extends Controller {
  static targets = ["content"]

  connect() {
    useTransition(this, {
      element: this.contentTarget,
      hiddenClass: 'hidden',
      transitioned: false,
    });
    useClickOutside(this)
  }

  closeWithKeyboard(event) {
    event.preventDefault()
    event.stopPropagation()

    this.close()
  }

  clickOutside(event) {
    this.close()
  }

  toggle() {
    this.toggleTransition();
  }

  open() {
    this.enter();
  }

  close() {
    if (!this.hasContentTarget) return

    this.leave();
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment