openproject-primer_view_components 0.68.0 → 0.69.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/app/assets/javascripts/components/primer/alpha/action_menu/action_menu_element.d.ts +5 -0
  4. data/app/assets/javascripts/components/primer/alpha/action_menu/action_menu_focus_zone_stack.d.ts +17 -0
  5. data/app/assets/javascripts/primer_view_components.js +1 -1
  6. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  7. data/app/components/primer/alpha/action_menu/action_menu_element.d.ts +5 -0
  8. data/app/components/primer/alpha/action_menu/action_menu_element.js +111 -16
  9. data/app/components/primer/alpha/action_menu/action_menu_element.ts +136 -23
  10. data/app/components/primer/alpha/action_menu/action_menu_focus_zone_stack.d.ts +17 -0
  11. data/app/components/primer/alpha/action_menu/action_menu_focus_zone_stack.js +62 -0
  12. data/app/components/primer/alpha/action_menu/action_menu_focus_zone_stack.ts +67 -0
  13. data/app/components/primer/alpha/action_menu/list.rb +3 -1
  14. data/app/components/primer/alpha/action_menu/list_wrapper.rb +31 -0
  15. data/app/components/primer/alpha/action_menu/menu.html.erb +24 -0
  16. data/app/components/primer/alpha/action_menu/menu.rb +136 -0
  17. data/app/components/primer/alpha/action_menu/primary_menu.rb +86 -0
  18. data/app/components/primer/alpha/action_menu/sub_menu.rb +74 -0
  19. data/app/components/primer/alpha/action_menu/sub_menu_item.html.erb +5 -0
  20. data/app/components/primer/alpha/action_menu/sub_menu_item.rb +53 -0
  21. data/app/components/primer/alpha/action_menu.html.erb +1 -26
  22. data/app/components/primer/alpha/action_menu.rb +44 -118
  23. data/app/components/primer/alpha/select_panel.rb +3 -3
  24. data/lib/primer/view_components/version.rb +2 -2
  25. data/previews/primer/alpha/action_menu_preview/multiple_select_form.html.erb +13 -4
  26. data/previews/primer/alpha/action_menu_preview/opens_dialog.html.erb +20 -11
  27. data/previews/primer/alpha/action_menu_preview/single_select_form_items.html.erb +13 -2
  28. data/previews/primer/alpha/action_menu_preview/sub_menus.html.erb +19 -0
  29. data/previews/primer/alpha/action_menu_preview/with_actions.html.erb +20 -11
  30. data/previews/primer/alpha/action_menu_preview/with_deferred_content.html.erb +24 -0
  31. data/previews/primer/alpha/action_menu_preview.rb +93 -29
  32. data/static/arguments.json +169 -68
  33. data/static/audited_at.json +4 -0
  34. data/static/constants.json +27 -7
  35. data/static/info_arch.json +797 -199
  36. data/static/previews.json +13 -0
  37. data/static/statuses.json +4 -0
  38. metadata +14 -2
@@ -171,143 +171,69 @@ module Primer
171
171
  class ActionMenu < Primer::Component
172
172
  status :alpha
173
173
 
174
- DEFAULT_PRELOAD = false
174
+ delegate :preload, :preload?, :list, to: :@primary_menu
175
+ delegate :with_show_button, :with_item, :items, :with_divider, :with_avatar_item, :with_group, :with_sub_menu_item, to: :@primary_menu
175
176
 
176
- DEFAULT_SELECT_VARIANT = :none
177
- SELECT_VARIANT_OPTIONS = [
178
- :single,
179
- :multiple,
180
- DEFAULT_SELECT_VARIANT
181
- ].freeze
182
-
183
- attr_reader :list, :preload
184
-
185
- alias preload? preload
186
-
187
- # @param menu_id [String] Id of the menu.
188
- # @param anchor_align [Symbol] <%= one_of(Primer::Alpha::Overlay::ANCHOR_ALIGN_OPTIONS) %>.
189
- # @param anchor_side [Symbol] <%= one_of(Primer::Alpha::Overlay::ANCHOR_SIDE_OPTIONS) %>.
190
- # @param size [Symbol] <%= one_of(Primer::Alpha::Overlay::SIZE_OPTIONS) %>.
191
- # @param src [String] Used with an `include-fragment` element to load menu content from the given source URL.
192
- # @param preload [Boolean] When true, and src is present, loads the `include-fragment` on trigger hover.
193
- # @param dynamic_label [Boolean] Whether or not to display the text of the currently selected item in the show button.
194
- # @param dynamic_label_prefix [String] If provided, the prefix is prepended to the dynamic label and displayed in the show button.
195
- # @param select_variant [Symbol] <%= one_of(Primer::Alpha::ActionMenu::SELECT_VARIANT_OPTIONS) %>
196
- # @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.
197
- # @param overlay_arguments [Hash] Arguments to pass to the underlying <%= link_to_component(Primer::Alpha::Overlay) %>
198
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>.
199
- def initialize(
200
- menu_id: self.class.generate_id,
201
- anchor_align: Primer::Alpha::Overlay::DEFAULT_ANCHOR_ALIGN,
202
- anchor_side: Primer::Alpha::Overlay::DEFAULT_ANCHOR_SIDE,
203
- size: Primer::Alpha::Overlay::DEFAULT_SIZE,
204
- src: nil,
205
- preload: DEFAULT_PRELOAD,
206
- dynamic_label: false,
207
- dynamic_label_prefix: nil,
208
- select_variant: DEFAULT_SELECT_VARIANT,
209
- form_arguments: {},
210
- overlay_arguments: {},
211
- **system_arguments
212
- )
213
- @menu_id = menu_id
214
- @src = src
215
- @preload = fetch_or_fallback_boolean(preload, DEFAULT_PRELOAD)
216
- @system_arguments = deny_tag_argument(**system_arguments)
217
-
218
- @system_arguments[:preload] = true if @src.present? && preload?
177
+ # @!parse
178
+ # # Adds an item to the menu.
179
+ # #
180
+ # # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList) %>'s `item` slot.
181
+ # def with_item(**system_arguments)
182
+ # end
183
+ #
184
+ # # Adds an avatar item to the menu.
185
+ # #
186
+ # # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList) %>'s `item` slot.
187
+ # def with_avatar_item(**system_arguments)
188
+ # end
189
+ #
190
+ # # Adds a divider to the list. Dividers visually separate items.
191
+ # #
192
+ # # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Divider) %>.
193
+ # def with_divider(**system_arguments)
194
+ # end
195
+ #
196
+ # # Adds a group to the menu. Groups are a logical set of items with a header.
197
+ # #
198
+ # # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionMenu::Group) %>.
199
+ # def with_group(**system_arguments)
200
+ # end
201
+ #
202
+ # # Gets the list of configured menu items, which includes regular items, avatar items, groups, and dividers.
203
+ # #
204
+ # # @return [Array<ViewComponent::Slot>]
205
+ # def items
206
+ # end
219
207
 
220
- @select_variant = fetch_or_fallback(SELECT_VARIANT_OPTIONS, select_variant, DEFAULT_SELECT_VARIANT)
208
+ # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionMenu::PrimaryMenu) %>.
209
+ def initialize(**system_arguments)
210
+ @primary_menu = PrimaryMenu.allocate
211
+ @system_arguments = @primary_menu.send(:initialize, **system_arguments)
221
212
 
222
213
  @system_arguments[:tag] = :"action-menu"
223
- @system_arguments[:"data-select-variant"] = select_variant
224
- @system_arguments[:"data-dynamic-label"] = "" if dynamic_label
225
- @system_arguments[:"data-dynamic-label-prefix"] = dynamic_label_prefix if dynamic_label_prefix.present?
214
+ @system_arguments[:preload] = true if @primary_menu.preload?
226
215
 
227
- overlay_arguments[:data] = merge_data(
228
- overlay_arguments, data: {
229
- target: "action-menu.overlay"
216
+ @system_arguments[:data] = merge_data(
217
+ @system_arguments, {
218
+ data: { "select-variant": @primary_menu.select_variant }
230
219
  }
231
220
  )
232
221
 
233
- @overlay = Primer::Alpha::Overlay.new(
234
- id: "#{@menu_id}-overlay",
235
- title: "Menu",
236
- visually_hide_title: true,
237
- anchor_align: anchor_align,
238
- anchor_side: anchor_side,
239
- size: size,
240
- **overlay_arguments
241
- )
222
+ @system_arguments[:"data-dynamic-label"] = "" if @primary_menu.dynamic_label
242
223
 
243
- @list = Primer::Alpha::ActionMenu::List.new(
244
- menu_id: @menu_id,
245
- select_variant: select_variant,
246
- form_arguments: form_arguments
247
- )
248
- end
249
-
250
- # @!parse
251
- # # Button to activate the menu.
252
- # #
253
- # # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::Overlay) %>'s `show_button` slot.
254
- # renders_one(:show_button)
255
-
256
- # Button to activate the menu.
257
- #
258
- # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::Overlay) %>'s `show_button` slot.
259
- def with_show_button(**system_arguments, &block)
260
- @overlay.with_show_button(**system_arguments, id: "#{@menu_id}-button", controls: "#{@menu_id}-list") do |button|
261
- evaluate_block(button, &block)
224
+ if @primary_menu.dynamic_label_prefix.present?
225
+ @system_arguments[:"data-dynamic-label-prefix"] = @primary_menu.dynamic_label_prefix
262
226
  end
263
227
  end
264
228
 
265
- # @!parse
266
- # # Adds a new item to the list.
267
- # #
268
- # # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Item) %>.
269
- # renders_many(:items)
270
-
271
- # Adds a new item to the list.
272
- #
273
- # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Item) %>.
274
- def with_item(**system_arguments, &block)
275
- @list.with_item(**system_arguments, &block)
276
- end
277
-
278
- # Adds a divider to the list.
279
- #
280
- # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList) %>'s `divider` slot.
281
- def with_divider(**system_arguments, &block)
282
- @list.with_divider(**system_arguments, &block)
283
- end
284
-
285
- # Adds an avatar item to the list. Avatar items are a convenient way to accessibly add an item with a leading avatar image.
286
- #
287
- # @param src [String] The source url of the avatar image.
288
- # @param username [String] The username associated with the avatar.
289
- # @param full_name [String] Optional. The user's full name.
290
- # @param full_name_scheme [Symbol] Optional. How to display the user's full name.
291
- # @param avatar_arguments [Hash] Optional. The arguments accepted by <%= link_to_component(Primer::Beta::Avatar) %>.
292
- # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Item) %>.
293
- def with_avatar_item(**system_arguments, &block)
294
- @list.with_avatar_item(**system_arguments, &block)
295
- end
296
-
297
- def with_group(**system_arguments, &block)
298
- @list.with_group(**system_arguments, &block)
299
- end
300
-
301
229
  private
302
230
 
303
231
  def before_render
304
232
  content
305
-
306
- raise ArgumentError, "`items` cannot be set when `src` is specified" if @src.present? && @list.items.any?
307
233
  end
308
234
 
309
235
  def render?
310
- @list.items.any? || @src.present?
236
+ @primary_menu.items.any? || @primary_menu.src.present?
311
237
  end
312
238
  end
313
239
  end
@@ -312,7 +312,7 @@ module Primer
312
312
  # @return [String]
313
313
  attr_reader :body_id
314
314
 
315
- # <%= one_of(Primer::Alpha::ActionMenu::SELECT_VARIANT_OPTIONS) %>
315
+ # <%= one_of(Primer::Alpha::ActionMenu::Menu::SELECT_VARIANT_OPTIONS) %>
316
316
  #
317
317
  # @return [Symbol]
318
318
  attr_reader :select_variant
@@ -461,7 +461,7 @@ module Primer
461
461
 
462
462
  @list = Primer::Alpha::SelectPanel::ItemList.new(
463
463
  **list_arguments,
464
- form_arguments: @list_form_arguments,
464
+ form_arguments: @list_form_arguments,
465
465
  id: "#{@panel_id}-list",
466
466
  select_variant: @select_variant,
467
467
  aria: {
@@ -546,4 +546,4 @@ module Primer
546
546
  end
547
547
  end
548
548
  end
549
- end
549
+ end
@@ -5,8 +5,8 @@ module Primer
5
5
  module ViewComponents
6
6
  module VERSION
7
7
  MAJOR = 0
8
- MINOR = 68
9
- PATCH = 0
8
+ MINOR = 69
9
+ PATCH = 1
10
10
 
11
11
  STRING = [MAJOR, MINOR, PATCH].join(".")
12
12
  end
@@ -1,10 +1,19 @@
1
1
  <%= form_with(url: action_menu_form_action_path(format: route_format)) do |f| %>
2
+ <% content = -> (base) do %>
3
+ <% base.with_item(label: "Fast forward", data: { value: "fast_forward" }) %>
4
+ <% base.with_item(label: "Recursive", data: { value: "recursive" }) %>
5
+ <% base.with_item(label: "Ours", data: { value: "ours" }, active: true) %>
6
+ <% base.with_item(label: "Resolve") %>
7
+ <% end %>
2
8
  <%= render(Primer::Alpha::ActionMenu.new(select_variant: :multiple, dynamic_label: true, dynamic_label_prefix: "Strategy", form_arguments: { builder: f, name: "foo" })) do |menu| %>
3
9
  <% menu.with_show_button { "Strategy" } %>
4
- <% menu.with_item(label: "Fast forward", data: { value: "fast_forward" }) %>
5
- <% menu.with_item(label: "Recursive", data: { value: "recursive" }) %>
6
- <% menu.with_item(label: "Ours", data: { value: "ours" }, active: true) %>
7
- <% menu.with_item(label: "Resolve") %>
10
+ <% if nest_in_sub_menu %>
11
+ <% menu.with_sub_menu_item(label: "Sub-menu") do |sub_menu| %>
12
+ <% content.call(sub_menu) %>
13
+ <% end %>
14
+ <% else %>
15
+ <% content.call(menu) %>
16
+ <% end %>
8
17
  <% end %>
9
18
  <hr>
10
19
  <div>
@@ -1,20 +1,29 @@
1
1
  <%= render(Primer::Alpha::ActionMenu.new) do |component| %>
2
2
  <% component.with_show_button { "Menu" } %>
3
- <% component.with_item(label: "Item", tag: :button, value: "") %>
4
- <% component.with_item(
5
- label: "Show dialog",
6
- tag: :button,
7
- content_arguments: { "data-show-dialog-id": "my-dialog" },
8
- value: "",
9
- scheme: :danger
10
- ) %>
3
+ <% contents = -> (base) do %>
4
+ <% base.with_item(label: "Item", tag: :button, value: "") %>
5
+ <% base.with_item(
6
+ label: "Show dialog",
7
+ tag: :button,
8
+ content_arguments: { "data-show-dialog-id": "my-dialog" },
9
+ value: "",
10
+ scheme: :danger
11
+ ) %>
12
+ <% end %>
13
+ <% if nest_in_sub_menu %>
14
+ <% component.with_sub_menu_item(label: "Sub-menu") do |sub_menu_item| %>
15
+ <% contents.call(sub_menu_item) %>
16
+ <% end %>
17
+ <% else %>
18
+ <% contents.call(component) %>
19
+ <% end %>
11
20
  <% end %>
12
21
 
13
- <%= render(Primer::Alpha::Dialog.new(id: "my-dialog", title: "Confirm deletion")) do |d| %>
14
- <%= render(Primer::Alpha::Dialog::Body.new()) do %>
22
+ <%= render(Primer::Alpha::Dialog.new(id: "my-dialog", title: "Confirm deletion")) do |dialog| %>
23
+ <% dialog.with_body do %>
15
24
  Are you sure you want to delete this?
16
25
  <% end %>
17
- <%= render(Primer::Alpha::Dialog::Footer.new()) do %>
26
+ <% dialog.with_footer do %>
18
27
  <%= render(Primer::Beta::Button.new(data: { "close-dialog-id": "my-dialog" })) { "Cancel" } %>
19
28
  <%= render(Primer::Beta::Button.new(scheme: :danger)) { "Delete" } %>
20
29
  <% end %>
@@ -1,5 +1,4 @@
1
- <%= render(Primer::Alpha::ActionMenu.new(select_variant: :single)) do |menu| %>
2
- <% menu.with_show_button { "Group By" } %>
1
+ <% contents = -> (menu) do %>
3
2
  <% menu.with_item(
4
3
  label: "Repository",
5
4
  href: action_menu_form_action_path(format: route_format),
@@ -29,3 +28,15 @@
29
28
  }
30
29
  ) %>
31
30
  <% end %>
31
+
32
+ <%= render(Primer::Alpha::ActionMenu.new(select_variant: :single)) do |menu| %>
33
+ <% menu.with_show_button { "Group By" } %>
34
+
35
+ <% if nest_in_sub_menu %>
36
+ <% menu.with_sub_menu_item(label: "Sub-menu") do |sub_menu_item| %>
37
+ <% contents.call(sub_menu_item) %>
38
+ <% end %>
39
+ <% else %>
40
+ <% contents.call(menu) %>
41
+ <% end %>
42
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <%# Center the invoker button to give left-appearing sub-menus enough space %>
2
+ <div style="<%= sub_menu_anchor_side.to_s.include?("left") ? "text-align: center" : "" %>">
3
+ <%= render(Primer::Alpha::ActionMenu.new(anchor_align: anchor_align, anchor_side: anchor_side)) do |menu| %>
4
+ <% menu.with_show_button { "Edit" } %>
5
+ <% menu.with_item(label: "Cut") %>
6
+ <% menu.with_item(label: "Copy") %>
7
+ <% menu.with_sub_menu_item(label: "Paste special", anchor_align: sub_menu_anchor_align, anchor_side: sub_menu_anchor_side) do |sub_menu| %>
8
+ <% sub_menu.with_leading_visual_icon(icon: :"sparkle-fill") %>
9
+ <% sub_menu.with_item(label: "Paste plain text") %>
10
+ <% sub_menu.with_item(label: "Paste formulas") %>
11
+ <% sub_menu.with_item(label: "Paste with formatting") %>
12
+ <% sub_menu.with_sub_menu_item(label: "Paste from") do |sub_menu| %>
13
+ <% sub_menu.with_item(label: "Current clipboard") %>
14
+ <% sub_menu.with_item(label: "History") %>
15
+ <% sub_menu.with_item(label: "Another device") %>
16
+ <% end %>
17
+ <% end %>
18
+ <% end %>
19
+ </div>
@@ -8,15 +8,24 @@
8
8
 
9
9
  <%= render(Primer::Alpha::ActionMenu.new) do |component| %>
10
10
  <% component.with_show_button { "Trigger" } %>
11
- <% component.with_item(label: "Alert", tag: :button, id: "alert-item", disabled: disable_items) %>
12
- <% component.with_item(label: "Navigate", tag: :a, content_arguments: { href: action_menu_landing_path }, disabled: disable_items) %>
13
- <% component.with_item(label: "Copy text", tag: :"clipboard-copy", content_arguments: { value: "Text to copy" }, disabled: disable_items) %>
14
- <% component.with_item(
15
- label: "Submit form",
16
- href: action_menu_form_action_path(format: route_format),
17
- form_arguments: {
18
- name: "foo", value: "bar", method: :post
19
- },
20
- disabled: disable_items
21
- ) %>
11
+ <% contents = -> (base) do %>
12
+ <% base.with_item(label: "Alert", tag: :button, id: "alert-item", disabled: disable_items) %>
13
+ <% base.with_item(label: "Navigate", tag: :a, content_arguments: { href: action_menu_landing_path }, disabled: disable_items) %>
14
+ <% base.with_item(label: "Copy text", tag: :"clipboard-copy", content_arguments: { value: "Text to copy" }, disabled: disable_items) %>
15
+ <% base.with_item(
16
+ label: "Submit form",
17
+ href: action_menu_form_action_path(format: route_format),
18
+ form_arguments: {
19
+ name: "foo", value: "bar", method: :post
20
+ },
21
+ disabled: disable_items
22
+ ) %>
23
+ <% end %>
24
+ <% if nest_in_sub_menu %>
25
+ <% component.with_sub_menu_item(label: "Sub-menu") do |sub_menu_item| %>
26
+ <% contents.call(sub_menu_item) %>
27
+ <% end %>
28
+ <% else %>
29
+ <% contents.call(component) %>
30
+ <% end %>
22
31
  <% end %>
@@ -0,0 +1,24 @@
1
+ <% if nest_in_sub_menu %>
2
+ <%= render(Primer::Alpha::ActionMenu.new) do |menu| %>
3
+ <% menu.with_show_button { "Menu with deferred content" } %>
4
+ <% menu.with_sub_menu_item(label: "Sub-menu", menu_id: "deferred", src: action_menu_deferred_path) %>
5
+ <% end %>
6
+ <% else %>
7
+ <%= render(Primer::Alpha::ActionMenu.new(menu_id: "deferred", src: action_menu_deferred_path)) do |menu| %>
8
+ <% menu.with_show_button { "Menu with deferred content" } %>
9
+ <% end %>
10
+ <% end %>
11
+
12
+ <%# This is used by the items rendered by ActionMenuController#deferred. It needs to be rendered %>
13
+ <%# here and not returned alongside async-rendered menu items in case of sub-menu nesting. Any %>
14
+ <%# additional HTML like this will end up being wrapped in a <ul> that's invisible when the menu is %>
15
+ <%# closed, and appear to never open. %>
16
+ <%= render(Primer::Alpha::Dialog.new(id: "my-dialog", title: "Confirm deletion")) do |d| %>
17
+ <%= render(Primer::Alpha::Dialog::Body.new()) do %>
18
+ Are you sure you want to delete this?
19
+ <% end %>
20
+ <%= render(Primer::Alpha::Dialog::Footer.new()) do %>
21
+ <%= render(Primer::Beta::Button.new(data: { "close-dialog-id": "my-dialog" })) { "Cancel" } %>
22
+ <%= render(Primer::Beta::Button.new(scheme: :danger)) { "Delete" } %>
23
+ <% end %>
24
+ <% end %>
@@ -11,7 +11,9 @@ module Primer
11
11
  # @param anchor_side [Symbol] select [outside_bottom, outside_top, outside_left, outside_right]
12
12
  # @param size [Symbol] select [auto, small, medium, large, xlarge]
13
13
  def playground(
14
- select_variant: Primer::Alpha::ActionMenu::DEFAULT_SELECT_VARIANT, anchor_align: Primer::Alpha::Overlay::DEFAULT_ANCHOR_ALIGN, anchor_side: Primer::Alpha::Overlay::DEFAULT_ANCHOR_SIDE,
14
+ select_variant: Primer::Alpha::ActionMenu::PrimaryMenu::DEFAULT_SELECT_VARIANT,
15
+ anchor_align: Primer::Alpha::Overlay::DEFAULT_ANCHOR_ALIGN,
16
+ anchor_side: Primer::Alpha::Overlay::DEFAULT_ANCHOR_SIDE,
15
17
  size: Primer::Alpha::Overlay::DEFAULT_SIZE
16
18
  )
17
19
  render(Primer::Alpha::ActionMenu.new(select_variant: select_variant, anchor_align: anchor_align, anchor_side: anchor_side, size: size)) do |menu|
@@ -55,13 +57,9 @@ module Primer
55
57
  # @label With groups
56
58
  #
57
59
  # @snapshot interactive
58
- def with_groups
59
- render(Primer::Alpha::ActionMenu.new(menu_id: "menu-1")) do |menu|
60
- menu.with_show_button do |button|
61
- button.with_trailing_action_icon(icon: :"triangle-down")
62
- "Favorite character"
63
- end
64
-
60
+ # @param nest_in_sub_menu toggle
61
+ def with_groups(nest_in_sub_menu: false)
62
+ contents = -> (menu) {
65
63
  menu.with_group do |group|
66
64
  group.with_heading(title: "Battlestar Galactica")
67
65
  group.with_item(label: "William Adama")
@@ -85,6 +83,21 @@ module Primer
85
83
  group.with_item(label: "Han Solo")
86
84
  group.with_item(label: "Chewbacca")
87
85
  end
86
+ }
87
+
88
+ render(Primer::Alpha::ActionMenu.new(menu_id: "menu-1")) do |menu|
89
+ menu.with_show_button do |button|
90
+ button.with_trailing_action_icon(icon: :"triangle-down")
91
+ "Favorite character"
92
+ end
93
+
94
+ if nest_in_sub_menu
95
+ menu.with_sub_menu_item(label: "Sub-menu") do |sub_menu_item|
96
+ contents.call(sub_menu_item)
97
+ end
98
+ else
99
+ contents.call(menu)
100
+ end
88
101
  end
89
102
  end
90
103
 
@@ -159,10 +172,9 @@ module Primer
159
172
 
160
173
  # @label Multiple select
161
174
  #
162
- def multiple_select
163
- render(Primer::Alpha::ActionMenu.new(select_variant: :multiple)) do |menu|
164
- menu.with_show_button { "Menu" }
165
-
175
+ # @param nest_in_sub_menu toggle
176
+ def multiple_select(nest_in_sub_menu: false)
177
+ content = -> (menu) {
166
178
  menu.with_avatar_item(
167
179
  src: "https://avatars.githubusercontent.com/u/18661030?v=4",
168
180
  username: "langermank",
@@ -189,6 +201,18 @@ module Primer
189
201
  avatar_arguments: { shape: :square },
190
202
  item_id: :armagan
191
203
  )
204
+ }
205
+
206
+ render(Primer::Alpha::ActionMenu.new(select_variant: :multiple)) do |menu|
207
+ menu.with_show_button { "Menu" }
208
+
209
+ if nest_in_sub_menu
210
+ menu.with_sub_menu_item(label: "Sub-menu") do |sub_menu|
211
+ content.call(sub_menu)
212
+ end
213
+ else
214
+ content.call(menu)
215
+ end
192
216
  end
193
217
  end
194
218
 
@@ -218,17 +242,29 @@ module Primer
218
242
  # @label Single Select with Internal Label
219
243
  #
220
244
  # @snapshot interactive
221
- def single_select_with_internal_label
245
+ # @param nest_in_sub_menu toggle
246
+ def single_select_with_internal_label(nest_in_sub_menu: false)
247
+ contents = -> (menu) {
248
+ menu.with_item(label: "Copy link") do |item|
249
+ item.with_trailing_visual_label(scheme: :accent, inline: true).with_content("Recommended")
250
+ end
251
+ menu.with_item(label: "Quote reply", active: true)
252
+ menu.with_item(label: "Reference in new issue")
253
+ }
254
+
222
255
  render(Primer::Alpha::ActionMenu.new(select_variant: :single, dynamic_label: true, dynamic_label_prefix: "Menu")) do |menu|
223
256
  menu.with_show_button do |button|
224
257
  button.with_trailing_action_icon(icon: :"triangle-down")
225
258
  "Menu"
226
259
  end
227
- menu.with_item(label: "Copy link") do |item|
228
- item.with_trailing_visual_label(scheme: :accent, inline: true).with_content("Recommended")
260
+
261
+ if nest_in_sub_menu
262
+ menu.with_sub_menu_item(label: "Sub-menu") do |sub_menu_item|
263
+ contents.call(sub_menu_item)
264
+ end
265
+ else
266
+ contents.call(menu)
229
267
  end
230
- menu.with_item(label: "Quote reply", active: true)
231
- menu.with_item(label: "Reference in new issue")
232
268
  end
233
269
  end
234
270
 
@@ -266,10 +302,9 @@ module Primer
266
302
 
267
303
  # @label With deferred content
268
304
  #
269
- def with_deferred_content
270
- render(Primer::Alpha::ActionMenu.new(menu_id: "deferred", src: UrlHelpers.action_menu_deferred_path)) do |menu|
271
- menu.with_show_button { "Menu with deferred content" }
272
- end
305
+ # @param nest_in_sub_menu toggle
306
+ def with_deferred_content(nest_in_sub_menu: false)
307
+ render_with_template(locals: { nest_in_sub_menu: nest_in_sub_menu })
273
308
  end
274
309
 
275
310
  # @label With deferred preloaded content
@@ -283,8 +318,13 @@ module Primer
283
318
  # @label With actions
284
319
  #
285
320
  # @param disable_items toggle
286
- def with_actions(disable_items: false, route_format: :html)
287
- render_with_template(locals: { disable_items: disable_items, route_format: route_format })
321
+ # @param nest_in_sub_menu toggle
322
+ def with_actions(disable_items: false, nest_in_sub_menu: false, route_format: :html)
323
+ render_with_template(locals: {
324
+ disable_items: disable_items,
325
+ nest_in_sub_menu: nest_in_sub_menu,
326
+ route_format: route_format
327
+ })
288
328
  end
289
329
 
290
330
  # @label Single select form
@@ -295,14 +335,16 @@ module Primer
295
335
 
296
336
  # @label Single select form items
297
337
  #
298
- def single_select_form_items(route_format: :html)
299
- render_with_template(locals: { route_format: route_format })
338
+ # @param nest_in_sub_menu toggle
339
+ def single_select_form_items(route_format: :html, nest_in_sub_menu: false)
340
+ render_with_template(locals: { route_format: route_format, nest_in_sub_menu: nest_in_sub_menu })
300
341
  end
301
342
 
302
343
  # @label Multiple select form
303
344
  #
304
- def multiple_select_form(route_format: :html)
305
- render_with_template(locals: { route_format: route_format })
345
+ # @param nest_in_sub_menu toggle
346
+ def multiple_select_form(route_format: :html, nest_in_sub_menu: false)
347
+ render_with_template(locals: { route_format: route_format, nest_in_sub_menu: nest_in_sub_menu })
306
348
  end
307
349
 
308
350
  # @label With disabled items
@@ -317,9 +359,11 @@ module Primer
317
359
 
318
360
  # @label Opens a dialog
319
361
  #
320
- def opens_dialog(menu_id: "menu-1")
362
+ # @param nest_in_sub_menu toggle
363
+ def opens_dialog(menu_id: "menu-1", nest_in_sub_menu: false)
321
364
  render_with_template(locals: {
322
- menu_id: menu_id
365
+ menu_id: menu_id,
366
+ nest_in_sub_menu: nest_in_sub_menu
323
367
  })
324
368
  end
325
369
 
@@ -405,6 +449,26 @@ module Primer
405
449
  # @label Two menus
406
450
  #
407
451
  def two_menus; end
452
+
453
+ # @label Sub-menus
454
+ #
455
+ # @param anchor_align [Symbol] select [start, center, end]
456
+ # @param anchor_side [Symbol] select [outside_bottom, outside_top, outside_left, outside_right]
457
+ # @param sub_menu_anchor_align [Symbol] select [start, center, end]
458
+ # @param sub_menu_anchor_side [Symbol] select [outside_bottom, outside_top, outside_left, outside_right]
459
+ def sub_menus(
460
+ anchor_align: Primer::Alpha::ActionMenu::PrimaryMenu::DEFAULT_ANCHOR_ALIGN,
461
+ anchor_side: Primer::Alpha::ActionMenu::PrimaryMenu::DEFAULT_ANCHOR_SIDE,
462
+ sub_menu_anchor_align: Primer::Alpha::ActionMenu::SubMenu::DEFAULT_ANCHOR_ALIGN,
463
+ sub_menu_anchor_side: Primer::Alpha::ActionMenu::SubMenu::DEFAULT_ANCHOR_SIDE
464
+ )
465
+ render_with_template(locals: {
466
+ anchor_align: anchor_align.to_sym,
467
+ anchor_side: anchor_side.to_sym,
468
+ sub_menu_anchor_align: sub_menu_anchor_align,
469
+ sub_menu_anchor_side: sub_menu_anchor_side
470
+ })
471
+ end
408
472
  end
409
473
  end
410
474
  end