diff --git a/lib/ruby_ui/dropdown_menu/dropdown_menu_content.rb b/lib/ruby_ui/dropdown_menu/dropdown_menu_content.rb index 44b8e16d..07c2ec5e 100644 --- a/lib/ruby_ui/dropdown_menu/dropdown_menu_content.rb +++ b/lib/ruby_ui/dropdown_menu/dropdown_menu_content.rb @@ -15,7 +15,7 @@ def default_attrs data: { state: :open }, - class: "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-background p-1 text-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 w-56" + class: "relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-background p-1 text-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 w-56" } end end diff --git a/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js b/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js index 266c0fed..65a98557 100644 --- a/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js +++ b/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js @@ -2,7 +2,7 @@ import { Controller } from "@hotwired/stimulus"; import { computePosition, flip, shift, offset } from "@floating-ui/dom"; export default class extends Controller { - static targets = ["trigger", "content", "menuItem"]; + static targets = ["trigger", "content", "menuItem", "overlay"]; static values = { open: { type: Boolean, @@ -33,7 +33,7 @@ export default class extends Controller { onClickOutside(event) { if (!this.openValue) return; - if (this.element.contains(event.target)) return; + if (this.element.contains(event.target) && (!this.hasOverlayTarget || event.target !== this.overlayTarget)) return; event.preventDefault(); this.close(); @@ -49,12 +49,20 @@ export default class extends Controller { this.#addEventListeners(); this.#computeTooltip() this.contentTarget.classList.remove("hidden"); + + if (this.hasOverlayTarget) { + this.overlayTarget.classList.remove("hidden"); + } } close() { this.openValue = false; this.#removeEventListeners(); this.contentTarget.classList.add("hidden"); + + if (this.hasOverlayTarget) { + this.overlayTarget.classList.add("hidden"); + } } #handleKeydown(e) { diff --git a/lib/ruby_ui/dropdown_menu/dropdown_menu_overlay.rb b/lib/ruby_ui/dropdown_menu/dropdown_menu_overlay.rb new file mode 100644 index 00000000..31389227 --- /dev/null +++ b/lib/ruby_ui/dropdown_menu/dropdown_menu_overlay.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module RubyUI + class DropdownMenuOverlay < Base + def view_template + div(**attrs) + end + + private + + def default_attrs + { + data: { + ruby_ui__dropdown_menu_target: "overlay", + action: "click->ruby-ui--dropdown-menu#onClickOutside" + }, + class: "absolute fixed inset-0 z-40 bg-black opacity-20 hidden" + } + end + end +end diff --git a/test/ruby_ui/dropdown_menu_test.rb b/test/ruby_ui/dropdown_menu_test.rb index fce350c1..4271d3c3 100644 --- a/test/ruby_ui/dropdown_menu_test.rb +++ b/test/ruby_ui/dropdown_menu_test.rb @@ -6,6 +6,7 @@ class RubyUI::DropdownMenuTest < ComponentTest def test_render_with_all_items output = phlex do RubyUI.DropdownMenu do + RubyUI::DropdownMenuOverlay() RubyUI.DropdownMenuTrigger(class: "w-full") do RubyUI.Button(variant: :outline) { "Open" } end