Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/forty-ghosts-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/view-components': minor
---

Introduce nested menus for Primer::Alpha::ActionMenu
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 1 addition & 26 deletions app/components/primer/alpha/action_menu.html.erb
Original file line number Diff line number Diff line change
@@ -1,28 +1,3 @@
<%= render Primer::BaseComponent.new(**@system_arguments) do %>
<focus-group direction="vertical" mnemonics retain>
<%= render(@overlay) do |overlay| %>
<% overlay.with_body(padding: :none) do %>
<% if @src.present? %>
<%= render(Primer::Alpha::IncludeFragment.new(src: @src, loading: preload? ? :eager : :lazy, "data-target": "action-menu.includeFragment")) do %>
<%= render(Primer::Alpha::ActionMenu::List.new(id: "#{@menu_id}-list", menu_id: @menu_id)) do |list| %>
<% list.with_item(
aria: { disabled: true },
content_arguments: {
display: :flex,
align_items: :center,
justify_content: :center,
text_align: :center,
autofocus: true
}
) do %>
<%= render Primer::Beta::Spinner.new(aria: { label: "Loading content..." }) %>
<% end %>
<% end %>
<% end %>
<% else %>
<%= render(@list) %>
<% end %>
<% end %>
<% end %>
</focus-group>
<%= render(@primary_menu) %>
<% end %>
162 changes: 44 additions & 118 deletions app/components/primer/alpha/action_menu.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,143 +171,69 @@ module Alpha
class ActionMenu < Primer::Component
status :alpha

DEFAULT_PRELOAD = false
delegate :preload, :preload?, :list, to: :@primary_menu
delegate :with_show_button, :with_item, :items, :with_divider, :with_avatar_item, :with_group, :with_sub_menu_item, to: :@primary_menu

DEFAULT_SELECT_VARIANT = :none
SELECT_VARIANT_OPTIONS = [
:single,
:multiple,
DEFAULT_SELECT_VARIANT
].freeze

attr_reader :list, :preload

alias preload? preload

# @param menu_id [String] Id of the menu.
# @param anchor_align [Symbol] <%= one_of(Primer::Alpha::Overlay::ANCHOR_ALIGN_OPTIONS) %>.
# @param anchor_side [Symbol] <%= one_of(Primer::Alpha::Overlay::ANCHOR_SIDE_OPTIONS) %>.
# @param size [Symbol] <%= one_of(Primer::Alpha::Overlay::SIZE_OPTIONS) %>.
# @param src [String] Used with an `include-fragment` element to load menu content from the given source URL.
# @param preload [Boolean] When true, and src is present, loads the `include-fragment` on trigger hover.
# @param dynamic_label [Boolean] Whether or not to display the text of the currently selected item in the show button.
# @param dynamic_label_prefix [String] If provided, the prefix is prepended to the dynamic label and displayed in the show button.
# @param select_variant [Symbol] <%= one_of(Primer::Alpha::ActionMenu::SELECT_VARIANT_OPTIONS) %>
# @param form_arguments [Hash] Allows an `ActionMenu` to act as a select list in multi- and single-select modes. Pass the `builder:` and `name:` options to this hash. `builder:` should be an instance of `ActionView::Helpers::FormBuilder`, which are created by the standard Rails `#form_with` and `#form_for` helpers. The `name:` option is the desired name of the field that will be included in the params sent to the server on form submission.
# @param overlay_arguments [Hash] Arguments to pass to the underlying <%= link_to_component(Primer::Alpha::Overlay) %>
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>.
def initialize(
menu_id: self.class.generate_id,
anchor_align: Primer::Alpha::Overlay::DEFAULT_ANCHOR_ALIGN,
anchor_side: Primer::Alpha::Overlay::DEFAULT_ANCHOR_SIDE,
size: Primer::Alpha::Overlay::DEFAULT_SIZE,
src: nil,
preload: DEFAULT_PRELOAD,
dynamic_label: false,
dynamic_label_prefix: nil,
select_variant: DEFAULT_SELECT_VARIANT,
form_arguments: {},
overlay_arguments: {},
**system_arguments
)
@menu_id = menu_id
@src = src
@preload = fetch_or_fallback_boolean(preload, DEFAULT_PRELOAD)
@system_arguments = deny_tag_argument(**system_arguments)

@system_arguments[:preload] = true if @src.present? && preload?
# @!parse
# # Adds an item to the menu.
# #
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList) %>'s `item` slot.
# def with_item(**system_arguments)
# end
#
# # Adds an avatar item to the menu.
# #
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList) %>'s `item` slot.
# def with_avatar_item(**system_arguments)
# end
#
# # Adds a divider to the list. Dividers visually separate items.
# #
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Divider) %>.
# def with_divider(**system_arguments)
# end
#
# # Adds a group to the menu. Groups are a logical set of items with a header.
# #
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionMenu::Group) %>.
# def with_group(**system_arguments)
# end
#
# # Gets the list of configured menu items, which includes regular items, avatar items, groups, and dividers.
# #
# # @return [Array<ViewComponent::Slot>]
# def items
# end

@select_variant = fetch_or_fallback(SELECT_VARIANT_OPTIONS, select_variant, DEFAULT_SELECT_VARIANT)
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionMenu::PrimaryMenu) %>.
def initialize(**system_arguments)
@primary_menu = PrimaryMenu.allocate
@system_arguments = @primary_menu.send(:initialize, **system_arguments)

@system_arguments[:tag] = :"action-menu"
@system_arguments[:"data-select-variant"] = select_variant
@system_arguments[:"data-dynamic-label"] = "" if dynamic_label
@system_arguments[:"data-dynamic-label-prefix"] = dynamic_label_prefix if dynamic_label_prefix.present?
@system_arguments[:preload] = true if @primary_menu.preload?

overlay_arguments[:data] = merge_data(
overlay_arguments, data: {
target: "action-menu.overlay"
@system_arguments[:data] = merge_data(
@system_arguments, {
data: { "select-variant": @primary_menu.select_variant }
}
)

@overlay = Primer::Alpha::Overlay.new(
id: "#{@menu_id}-overlay",
title: "Menu",
visually_hide_title: true,
anchor_align: anchor_align,
anchor_side: anchor_side,
size: size,
**overlay_arguments
)
@system_arguments[:"data-dynamic-label"] = "" if @primary_menu.dynamic_label

@list = Primer::Alpha::ActionMenu::List.new(
menu_id: @menu_id,
select_variant: select_variant,
form_arguments: form_arguments
)
end

# @!parse
# # Button to activate the menu.
# #
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::Overlay) %>'s `show_button` slot.
# renders_one(:show_button)

# Button to activate the menu.
#
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::Overlay) %>'s `show_button` slot.
def with_show_button(**system_arguments, &block)
@overlay.with_show_button(**system_arguments, id: "#{@menu_id}-button", controls: "#{@menu_id}-list") do |button|
evaluate_block(button, &block)
if @primary_menu.dynamic_label_prefix.present?
@system_arguments[:"data-dynamic-label-prefix"] = @primary_menu.dynamic_label_prefix
end
end

# @!parse
# # Adds a new item to the list.
# #
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Item) %>.
# renders_many(:items)

# Adds a new item to the list.
#
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Item) %>.
def with_item(**system_arguments, &block)
@list.with_item(**system_arguments, &block)
end

# Adds a divider to the list.
#
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList) %>'s `divider` slot.
def with_divider(**system_arguments, &block)
@list.with_divider(**system_arguments, &block)
end

# Adds an avatar item to the list. Avatar items are a convenient way to accessibly add an item with a leading avatar image.
#
# @param src [String] The source url of the avatar image.
# @param username [String] The username associated with the avatar.
# @param full_name [String] Optional. The user's full name.
# @param full_name_scheme [Symbol] Optional. How to display the user's full name.
# @param avatar_arguments [Hash] Optional. The arguments accepted by <%= link_to_component(Primer::Beta::Avatar) %>.
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Item) %>.
def with_avatar_item(**system_arguments, &block)
@list.with_avatar_item(**system_arguments, &block)
end

def with_group(**system_arguments, &block)
@list.with_group(**system_arguments, &block)
end

private

def before_render
content

raise ArgumentError, "`items` cannot be set when `src` is specified" if @src.present? && @list.items.any?
end

def render?
@list.items.any? || @src.present?
@primary_menu.items.any? || @primary_menu.src.present?
end
end
end
Expand Down
Loading