- 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/[email protected]/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:[email protected]/dist/index.js"
- 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>
- 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();
}
}