Menu

Dropdown

Phlex Warning: Your `Components::Preview` class doesn't define a `view_template` method. If you are upgrading to Phlex 2.x make sure to rename your `template` method to `view_template`. See: https://beta.phlex.fun/guides/v2-upgrade.html

Installation

Add the component to your project

CLI

Run the following command in your terminal

bundle exec essence add dropdown
Manually

Copy and paste the following code into your project

components/dropdown.rb
# frozen_string_literal: true

class Components::Dropdown < Components::Essence
  BASE = "relative inline-block"
  TRIGGER_BASE = "cursor-pointer list-none"
  CONTENT_BASE = "hidden absolute mt-2 z-10 rounded-md bg-white border border-b-2 border-gray-200/75 p-1.5 flex-col min-w-48 w-fit right-0"
  ITEM_BASE = "flex items-center gap-2 p-1 w-full flex text-xs font-medium text-gray-700 group hover:bg-gray-50 transition duration-100 rounded-xs justify-start"
  SEPARATOR_BASE = "border-t border-gray-200/75 my-2 -mx-1.5"

  POSITIONS = {
    top: "bottom-full",
    right: "left-full",
    bottom: "top-full",
    left: "right-full"
  }

  attr_reader :attributes

  def initialize(**attributes)
    super(**attributes)
    @attributes[:class] = merge_classes(BASE, attributes[:class])
  end


  def view_template(&)
    div(**attributes, &)
  end

  def trigger(**mattributes, &)
    mattributes[:class] = merge_classes(TRIGGER_BASE, mattributes[:class])
    mattributes[:data] = { action: "click->dropdown#toggle click@window->dropdown#hide" }

    render Button.new(**mattributes, &)
  end

  def content(**mattributes, &)
    mattributes[:class] = merge_classes(CONTENT_BASE, mattributes[:class])
    mattributes[:data] = { dropdown_target: "content" }

    div(**mattributes, &)
  end

  def item(**mattributes, &)
    mattributes[:size] = :xs
    mattributes[:kind] = :ghost
    mattributes[:class] = merge_classes(ITEM_BASE, mattributes[:class])

    render Button.new(**mattributes, &)
  end

  def separator(**mattributes)
    mattributes[:class] = merge_classes(SEPARATOR_BASE, mattributes[:class])

    hr(**mattributes)
  end

  private

  def initialize_default_attributes
    {
      data: {
        controller: "dropdown"
      }
    }
  end
end
controllers/dropdown_controller.js
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["content"];

  connect() {}

  toggle() {
    this.contentTarget.classList.toggle("hidden");
  }

  hide(event) {
    if (
      !this.element.contains(event.target) &&
      !this.contentTarget.classList.contains("hidden")
    ) {
      this.contentTarget.classList.add("hidden");
    }
  }
}