primer_view_components 0.14.0 → 0.15.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -0
- data/app/assets/javascripts/app/components/primer/alpha/action_menu/action_menu_element.d.ts +20 -0
- data/app/assets/javascripts/app/components/primer/beta/nav_list.d.ts +0 -11
- data/app/assets/javascripts/app/components/primer/beta/nav_list_group_element.d.ts +19 -0
- data/app/assets/javascripts/app/components/primer/primer.d.ts +1 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/assets/styles/primer_view_components.css +1 -1
- data/app/assets/styles/primer_view_components.css.map +1 -1
- data/app/components/primer/alpha/action_list/item.rb +13 -1
- data/app/components/primer/alpha/action_list.css +1 -1
- data/app/components/primer/alpha/action_list.css.json +1 -0
- data/app/components/primer/alpha/action_list.css.map +1 -1
- data/app/components/primer/alpha/action_list.pcss +3 -1
- data/app/components/primer/alpha/action_list.rb +5 -4
- data/app/components/primer/alpha/action_menu/action_menu_element.d.ts +20 -0
- data/app/components/primer/alpha/action_menu/action_menu_element.js +85 -11
- data/app/components/primer/alpha/action_menu/action_menu_element.ts +110 -12
- data/app/components/primer/alpha/action_menu/group.rb +23 -0
- data/app/components/primer/alpha/action_menu/heading.rb +17 -0
- data/app/components/primer/alpha/action_menu/list.html.erb +1 -0
- data/app/components/primer/alpha/action_menu/list.rb +62 -51
- data/app/components/primer/alpha/action_menu/list_wrapper.rb +40 -0
- data/app/components/primer/alpha/action_menu.rb +38 -1
- data/app/components/primer/alpha/overlay.css +1 -1
- data/app/components/primer/alpha/overlay.css.json +2 -1
- data/app/components/primer/alpha/overlay.css.map +1 -1
- data/app/components/primer/alpha/overlay.pcss +6 -2
- data/app/components/primer/alpha/text_field.css.map +1 -1
- data/app/components/primer/alpha/text_field.pcss +2 -2
- data/app/components/primer/alpha/tool_tip.js +8 -4
- data/app/components/primer/alpha/tool_tip.ts +9 -4
- data/app/components/primer/beta/auto_complete/auto_complete.html.erb +1 -7
- data/app/components/primer/beta/auto_complete/item.rb +1 -1
- data/app/components/primer/beta/auto_complete.rb +6 -1
- data/app/components/primer/beta/base_button.rb +2 -3
- data/app/components/primer/beta/blankslate.css +1 -1
- data/app/components/primer/beta/blankslate.css.map +1 -1
- data/app/components/primer/beta/blankslate.pcss +3 -3
- data/app/components/primer/beta/button.css +1 -1
- data/app/components/primer/beta/button.css.json +9 -8
- data/app/components/primer/beta/button.css.map +1 -1
- data/app/components/primer/beta/button.pcss +15 -11
- data/app/components/primer/beta/nav_list/group.html.erb +7 -5
- data/app/components/primer/beta/nav_list/group.rb +2 -2
- data/app/components/primer/beta/nav_list.d.ts +0 -11
- data/app/components/primer/beta/nav_list.js +2 -85
- data/app/components/primer/beta/nav_list.ts +1 -85
- data/app/components/primer/beta/nav_list_group_element.d.ts +19 -0
- data/app/components/primer/beta/nav_list_group_element.js +108 -0
- data/app/components/primer/beta/nav_list_group_element.ts +97 -0
- data/app/components/primer/beta/relative_time.rb +4 -4
- data/app/components/primer/component.rb +3 -0
- data/app/components/primer/primer.d.ts +1 -0
- data/app/components/primer/primer.js +1 -0
- data/app/components/primer/primer.ts +1 -0
- data/app/lib/primer/experimental_render_helpers.rb +32 -0
- data/app/lib/primer/experimental_slot_helpers.rb +30 -0
- data/lib/primer/view_components/version.rb +2 -2
- data/lib/primer/yard/lookbook_pages_backend.rb +2 -2
- data/previews/primer/alpha/action_menu_preview.rb +73 -7
- data/previews/primer/alpha/dialog_preview/with_auto_complete.html.erb +8 -0
- data/previews/primer/alpha/dialog_preview.rb +17 -0
- data/previews/primer/beta/blankslate_preview/inside_flex_container.html.erb +6 -0
- data/previews/primer/beta/blankslate_preview.rb +3 -0
- data/previews/primer/beta/nav_list_preview.rb +10 -1
- data/static/arguments.json +92 -1
- data/static/audited_at.json +4 -1
- data/static/classes.json +3 -0
- data/static/constants.json +9 -0
- data/static/info_arch.json +256 -52
- data/static/previews.json +52 -0
- data/static/statuses.json +3 -0
- metadata +14 -2
@@ -12,6 +12,17 @@ type SelectedItem = {
|
|
12
12
|
const validSelectors = ['[role="menuitem"]', '[role="menuitemcheckbox"]', '[role="menuitemradio"]']
|
13
13
|
const menuItemSelectors = validSelectors.map(selector => `:not([hidden]) > ${selector}`)
|
14
14
|
|
15
|
+
export type ItemActivatedEvent = {
|
16
|
+
item: Element
|
17
|
+
checked: boolean
|
18
|
+
}
|
19
|
+
|
20
|
+
declare global {
|
21
|
+
interface HTMLElementEventMap {
|
22
|
+
itemActivated: CustomEvent<ItemActivatedEvent>
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
15
26
|
@controller
|
16
27
|
export class ActionMenuElement extends HTMLElement {
|
17
28
|
@target
|
@@ -82,7 +93,7 @@ export class ActionMenuElement extends HTMLElement {
|
|
82
93
|
results.push({
|
83
94
|
label: labelEl?.textContent,
|
84
95
|
value: selectedItem?.getAttribute('data-value'),
|
85
|
-
element: selectedItem
|
96
|
+
element: selectedItem,
|
86
97
|
})
|
87
98
|
}
|
88
99
|
|
@@ -102,35 +113,39 @@ export class ActionMenuElement extends HTMLElement {
|
|
102
113
|
|
103
114
|
if (this.includeFragment) {
|
104
115
|
this.includeFragment.addEventListener('include-fragment-replaced', this, {
|
105
|
-
signal
|
116
|
+
signal,
|
106
117
|
})
|
107
118
|
}
|
108
119
|
}
|
109
120
|
|
121
|
+
disconnectedCallback() {
|
122
|
+
this.#abortController.abort()
|
123
|
+
}
|
124
|
+
|
110
125
|
#softDisableItems() {
|
111
126
|
const {signal} = this.#abortController
|
112
127
|
|
113
|
-
for (const item of this
|
128
|
+
for (const item of this.querySelectorAll(validSelectors.join(','))) {
|
114
129
|
item.addEventListener('click', this.#potentiallyDisallowActivation.bind(this), {signal})
|
115
130
|
item.addEventListener('keydown', this.#potentiallyDisallowActivation.bind(this), {signal})
|
116
131
|
}
|
117
132
|
}
|
118
133
|
|
119
|
-
|
120
|
-
|
134
|
+
// returns true if activation was prevented
|
135
|
+
#potentiallyDisallowActivation(event: Event): boolean {
|
136
|
+
if (!this.#isActivation(event)) return false
|
121
137
|
|
122
138
|
const item = (event.target as HTMLElement).closest(menuItemSelectors.join(','))
|
123
|
-
if (!item) return
|
139
|
+
if (!item) return false
|
124
140
|
|
125
141
|
if (item.getAttribute('aria-disabled')) {
|
126
142
|
event.preventDefault()
|
127
143
|
event.stopPropagation()
|
128
144
|
event.stopImmediatePropagation()
|
145
|
+
return true
|
129
146
|
}
|
130
|
-
}
|
131
147
|
|
132
|
-
|
133
|
-
this.#abortController.abort()
|
148
|
+
return false
|
134
149
|
}
|
135
150
|
|
136
151
|
#isKeyboardActivation(event: Event): boolean {
|
@@ -202,6 +217,8 @@ export class ActionMenuElement extends HTMLElement {
|
|
202
217
|
const targetIsItem = item !== null
|
203
218
|
|
204
219
|
if (targetIsItem && eventIsActivation) {
|
220
|
+
if (this.#potentiallyDisallowActivation(event)) return
|
221
|
+
|
205
222
|
const dialogInvoker = item.closest('[data-show-dialog-id]')
|
206
223
|
|
207
224
|
if (dialogInvoker) {
|
@@ -214,7 +231,7 @@ export class ActionMenuElement extends HTMLElement {
|
|
214
231
|
}
|
215
232
|
|
216
233
|
this.#activateItem(event, item)
|
217
|
-
this.#handleItemActivated(
|
234
|
+
this.#handleItemActivated(item)
|
218
235
|
|
219
236
|
// Pressing the space key on a button or link will cause the page to scroll unless preventDefault()
|
220
237
|
// is called. While calling preventDefault() appears to have no effect on link navigation, it skips
|
@@ -263,7 +280,7 @@ export class ActionMenuElement extends HTMLElement {
|
|
263
280
|
dialog.addEventListener('cancel', handleDialogClose, {signal})
|
264
281
|
}
|
265
282
|
|
266
|
-
#handleItemActivated(
|
283
|
+
#handleItemActivated(item: Element) {
|
267
284
|
// Hide popover after current event loop to prevent changes in focus from
|
268
285
|
// altering the target of the event. Not doing this specifically affects
|
269
286
|
// <a> tags. It causes the event to be sent to the currently focused element
|
@@ -304,6 +321,11 @@ export class ActionMenuElement extends HTMLElement {
|
|
304
321
|
}
|
305
322
|
|
306
323
|
this.#updateInput()
|
324
|
+
this.dispatchEvent(
|
325
|
+
new CustomEvent('itemActivated', {
|
326
|
+
detail: {item: item.parentElement, checked: this.isItemChecked(item.parentElement)},
|
327
|
+
}),
|
328
|
+
)
|
307
329
|
}
|
308
330
|
|
309
331
|
#activateItem(event: Event, item: Element) {
|
@@ -410,9 +432,85 @@ export class ActionMenuElement extends HTMLElement {
|
|
410
432
|
return this.querySelector(menuItemSelectors.join(','))
|
411
433
|
}
|
412
434
|
|
413
|
-
get
|
435
|
+
get items(): HTMLElement[] {
|
414
436
|
return Array.from(this.querySelectorAll(menuItemSelectors.join(',')))
|
415
437
|
}
|
438
|
+
|
439
|
+
getItemById(itemId: string): HTMLElement | null {
|
440
|
+
return this.querySelector(`li[data-item-id="${itemId}"`)
|
441
|
+
}
|
442
|
+
|
443
|
+
isItemDisabled(item: Element | null): boolean {
|
444
|
+
if (item) {
|
445
|
+
return item.classList.contains('ActionListItem--disabled')
|
446
|
+
} else {
|
447
|
+
return false
|
448
|
+
}
|
449
|
+
}
|
450
|
+
|
451
|
+
disableItem(item: Element | null) {
|
452
|
+
if (item) {
|
453
|
+
item.classList.add('ActionListItem--disabled')
|
454
|
+
item.querySelector('.ActionListContent')!.setAttribute('aria-disabled', 'true')
|
455
|
+
}
|
456
|
+
}
|
457
|
+
|
458
|
+
enableItem(item: Element | null) {
|
459
|
+
if (item) {
|
460
|
+
item.classList.remove('ActionListItem--disabled')
|
461
|
+
item.querySelector('.ActionListContent')!.removeAttribute('aria-disabled')
|
462
|
+
}
|
463
|
+
}
|
464
|
+
|
465
|
+
isItemHidden(item: Element | null): boolean {
|
466
|
+
if (item) {
|
467
|
+
return item.hasAttribute('hidden')
|
468
|
+
} else {
|
469
|
+
return false
|
470
|
+
}
|
471
|
+
}
|
472
|
+
|
473
|
+
hideItem(item: Element | null) {
|
474
|
+
if (item) {
|
475
|
+
item.setAttribute('hidden', 'hidden')
|
476
|
+
}
|
477
|
+
}
|
478
|
+
|
479
|
+
showItem(item: Element | null) {
|
480
|
+
if (item) {
|
481
|
+
item.removeAttribute('hidden')
|
482
|
+
}
|
483
|
+
}
|
484
|
+
|
485
|
+
isItemChecked(item: Element | null) {
|
486
|
+
if (item) {
|
487
|
+
return item.querySelector('.ActionListContent')!.getAttribute('aria-checked') === 'true'
|
488
|
+
} else {
|
489
|
+
return false
|
490
|
+
}
|
491
|
+
}
|
492
|
+
|
493
|
+
checkItem(item: Element | null) {
|
494
|
+
if (item && (this.selectVariant === 'single' || this.selectVariant === 'multiple')) {
|
495
|
+
const itemContent = item.querySelector('.ActionListContent')!
|
496
|
+
const ariaChecked = itemContent.getAttribute('aria-checked') === 'true'
|
497
|
+
|
498
|
+
if (!ariaChecked) {
|
499
|
+
this.#handleItemActivated(itemContent)
|
500
|
+
}
|
501
|
+
}
|
502
|
+
}
|
503
|
+
|
504
|
+
uncheckItem(item: Element | null) {
|
505
|
+
if (item && (this.selectVariant === 'single' || this.selectVariant === 'multiple')) {
|
506
|
+
const itemContent = item.querySelector('.ActionListContent')!
|
507
|
+
const ariaChecked = itemContent.getAttribute('aria-checked') === 'true'
|
508
|
+
|
509
|
+
if (ariaChecked) {
|
510
|
+
this.#handleItemActivated(itemContent)
|
511
|
+
}
|
512
|
+
}
|
513
|
+
}
|
416
514
|
}
|
417
515
|
|
418
516
|
if (!window.customElements.get('action-menu')) {
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Primer
|
5
|
+
module Alpha
|
6
|
+
class ActionMenu
|
7
|
+
# This component is part of <%= link_to_component(Primer::Alpha::ActionMenu) %> and should not be
|
8
|
+
# used as a standalone component.
|
9
|
+
class Group < Primer::Alpha::ActionList
|
10
|
+
# Heading text rendered above the list of items.
|
11
|
+
#
|
12
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionMenu::Heading) %>.
|
13
|
+
def with_heading(**system_arguments, &block)
|
14
|
+
super(component_klass: Primer::Alpha::ActionMenu::Heading, **system_arguments, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def with_divider
|
18
|
+
raise "ActionMenu groups cannot have dividers"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Alpha
|
5
|
+
class ActionMenu
|
6
|
+
# Heading used to describe groups within an action menu.
|
7
|
+
class Heading < Primer::Alpha::ActionList::Heading
|
8
|
+
def initialize(**)
|
9
|
+
super
|
10
|
+
|
11
|
+
# Headings don't make sense in a menu context, so use div instead
|
12
|
+
@tag = :div
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render(@list) %>
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# typed: true
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Primer
|
@@ -6,72 +5,84 @@ module Primer
|
|
6
5
|
class ActionMenu
|
7
6
|
# This component is part of <%= link_to_component(Primer::Alpha::ActionMenu) %> and should not be
|
8
7
|
# used as a standalone component.
|
9
|
-
class List < Primer::
|
8
|
+
class List < Primer::Component
|
10
9
|
DEFAULT_ITEM_TAG = :button
|
11
10
|
ITEM_TAG_OPTIONS = [:a, :"clipboard-copy", DEFAULT_ITEM_TAG].freeze
|
12
11
|
|
13
|
-
|
14
|
-
#
|
15
|
-
# @param data [Hash] When the menu is used as a form input (see the <%= link_to_component(Primer::Alpha::ActionMenu) %> docs), the label is submitted to the server by default. However, if the `data: { value: }` or `"data-value":` attribute is provided, it will be sent to the server instead.
|
16
|
-
# @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Primer::Alpha::ActionList::Item) %>, or whatever class is passed as the `component_klass` argument.
|
17
|
-
def with_item(data: {}, **system_arguments, &block)
|
18
|
-
system_arguments = organize_arguments(data: data, **system_arguments)
|
12
|
+
attr_reader :items
|
19
13
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
14
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList) %>
|
15
|
+
def initialize(**system_arguments)
|
16
|
+
@items = []
|
17
|
+
@has_group = false
|
24
18
|
|
25
|
-
|
26
|
-
#
|
27
|
-
# @param src [String] The source url of the avatar image.
|
28
|
-
# @param username [String] The username associated with the avatar.
|
29
|
-
# @param full_name [String] Optional. The user's full name.
|
30
|
-
# @param full_name_scheme [Symbol] Optional. How to display the user's full name. <%= one_of(Primer::Alpha::ActionList::Item::DESCRIPTION_SCHEME_OPTIONS) %>
|
31
|
-
# @param data [Hash] When the menu is used as a form input (see the <%= link_to_component(Primer::Alpha::ActionMenu) %> docs), the label is submitted to the server by default. However, if the `data: { value: }` or `"data-value":` attribute is provided, it will be sent to the server instead.
|
32
|
-
# @param avatar_arguments [Hash] Optional. The arguments accepted by <%= link_to_component(Primer::Beta::Avatar) %>.
|
33
|
-
# @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Primer::Alpha::ActionList::Item) %>, or whatever class is passed as the `component_klass` argument.
|
34
|
-
def with_avatar_item(src:, username:, full_name: nil, full_name_scheme: Primer::Alpha::ActionList::Item::DEFAULT_DESCRIPTION_SCHEME, data: {}, avatar_arguments: {}, **system_arguments, &block)
|
35
|
-
system_arguments = organize_arguments(data: data, **system_arguments)
|
36
|
-
|
37
|
-
super(src: src, username: username, full_name: full_name, full_name_scheme: full_name_scheme, avatar_arguments: avatar_arguments, **system_arguments) do |item|
|
38
|
-
evaluate_block(item, &block)
|
39
|
-
end
|
19
|
+
@list = Primer::Alpha::ActionMenu::ListWrapper.new(**system_arguments)
|
40
20
|
end
|
41
21
|
|
42
|
-
|
43
|
-
|
44
|
-
def initialize(menu_id:, **system_arguments, &block)
|
45
|
-
@menu_id = menu_id
|
22
|
+
def with_group(**system_arguments, &block)
|
23
|
+
@has_group = true
|
46
24
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
25
|
+
@items << {
|
26
|
+
type: :group,
|
27
|
+
kwargs: system_arguments,
|
28
|
+
block: block
|
29
|
+
}
|
30
|
+
end
|
51
31
|
|
52
|
-
|
53
|
-
|
54
|
-
|
32
|
+
def with_item(**system_arguments, &block)
|
33
|
+
@items << {
|
34
|
+
type: :item,
|
35
|
+
kwargs: organize_arguments(**system_arguments),
|
36
|
+
block: block
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def with_avatar_item(**system_arguments, &block)
|
41
|
+
@items << {
|
42
|
+
type: :avatar_item,
|
43
|
+
kwargs: organize_arguments(**system_arguments),
|
44
|
+
block: block
|
45
|
+
}
|
46
|
+
end
|
55
47
|
|
56
|
-
|
48
|
+
def with_divider(**system_arguments, &block)
|
49
|
+
@items << {
|
50
|
+
type: :divider,
|
51
|
+
kwargs: system_arguments,
|
52
|
+
block: block
|
53
|
+
}
|
57
54
|
end
|
58
55
|
|
59
56
|
private
|
60
57
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
#
|
65
|
-
# Handle blocks that originate from C code such as `&:method` by checking
|
66
|
-
# source_location. Such blocks don't allow access to their receiver.
|
67
|
-
return unless block&.source_location
|
58
|
+
def contains_group?
|
59
|
+
@has_group
|
60
|
+
end
|
68
61
|
|
69
|
-
|
62
|
+
def before_render
|
63
|
+
content
|
64
|
+
|
65
|
+
@items.each do |item|
|
66
|
+
case item[:type]
|
67
|
+
when :divider, :group
|
68
|
+
add_item(item, to: @list)
|
69
|
+
else
|
70
|
+
if contains_group?
|
71
|
+
@list.with_group do |group|
|
72
|
+
add_item(item, to: group)
|
73
|
+
end
|
74
|
+
else
|
75
|
+
add_item(item, to: @list)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
70
80
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
81
|
+
def add_item(item, to:)
|
82
|
+
parent = to
|
83
|
+
mtd = :"with_#{item[:type]}"
|
84
|
+
parent.send(mtd, **item[:kwargs]) do |item_instance|
|
85
|
+
evaluate_block(item_instance, &item[:block])
|
75
86
|
end
|
76
87
|
end
|
77
88
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Alpha
|
5
|
+
class ActionMenu
|
6
|
+
# This component is part of <%= link_to_component(Primer::Alpha::ActionMenu) %> and should not be
|
7
|
+
# used as a standalone component.
|
8
|
+
class ListWrapper < Primer::Alpha::ActionList
|
9
|
+
add_polymorphic_slot_type(
|
10
|
+
slot_name: :items,
|
11
|
+
type: :group,
|
12
|
+
callable: lambda { |**system_arguments|
|
13
|
+
Primer::Alpha::ActionMenu::Group.new(
|
14
|
+
**system_arguments,
|
15
|
+
role: :group,
|
16
|
+
select_variant: @select_variant
|
17
|
+
)
|
18
|
+
}
|
19
|
+
)
|
20
|
+
|
21
|
+
# @param menu_id [String] ID of the parent menu.
|
22
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList) %>
|
23
|
+
def initialize(menu_id:, **system_arguments)
|
24
|
+
@menu_id = menu_id
|
25
|
+
|
26
|
+
system_arguments[:aria] = merge_aria(
|
27
|
+
system_arguments,
|
28
|
+
{ aria: { labelledby: "#{@menu_id}-button" } }
|
29
|
+
)
|
30
|
+
|
31
|
+
system_arguments[:role] = :menu
|
32
|
+
system_arguments[:scheme] = :inset
|
33
|
+
system_arguments[:id] = "#{@menu_id}-list"
|
34
|
+
|
35
|
+
super(**system_arguments)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -128,6 +128,39 @@ module Primer
|
|
128
128
|
#
|
129
129
|
# Additional information around the keyboard functionality and implementation can be found on the
|
130
130
|
# [WAI-ARIA Authoring Practices](https://www.w3.org/TR/wai-aria-practices-1.2/#menu).
|
131
|
+
#
|
132
|
+
# ### JavaScript API
|
133
|
+
#
|
134
|
+
# `ActionList`s render an `<action-list>` custom element that exposes behavior to the client. For all these methods,
|
135
|
+
# `itemId` refers to the value of the `item_id:` argument (see below) that is used to populate the `data-item-id` HTML
|
136
|
+
# attribute.
|
137
|
+
#
|
138
|
+
# #### Query methods
|
139
|
+
#
|
140
|
+
# * `getItemById(itemId: string): Element`: Returns the item's HTML `<li>` element. The return value can be passed as the `item` argument to the other methods listed below.
|
141
|
+
# * `isItemChecked(item: Element): boolean`: Returns `true` if the item is checked, `false` otherwise.
|
142
|
+
# * `isItemHidden(item: Element): boolean`: Returns `true` if the item is hidden, `false` otherwise.
|
143
|
+
# * `isItemDisabled(item: Element): boolean`: Returns `true` if the item is disabled, `false` otherwise.
|
144
|
+
#
|
145
|
+
# #### State methods
|
146
|
+
#
|
147
|
+
# * `showItem(item: Element)`: Shows the item, i.e. makes it visible.
|
148
|
+
# * `hideItem(item: Element)`: Hides the item, i.e. makes it invisible.
|
149
|
+
# * `enableItem(item: Element)`: Enables the item, i.e. makes it clickable by the mouse and keyboard.
|
150
|
+
# * `disableItem(item: Element)`: Disables the item, i.e. makes it unclickable by the mouse and keyboard.
|
151
|
+
# * `checkItem(item: Element)`: Checks the item. Only has an effect in single- and multi-select modes.
|
152
|
+
# * `uncheckItem(item: Element)`: Unchecks the item. Only has an effect in multi-select mode, since items cannot be unchecked in single-select mode.
|
153
|
+
#
|
154
|
+
# #### Events
|
155
|
+
#
|
156
|
+
# The `<action-menu>` element fires an `itemActivated` event whenever an item is activated (eg. clicked) via the mouse or keyboard.
|
157
|
+
#
|
158
|
+
# ```typescript
|
159
|
+
# document.querySelector("action-menu").addEventListener("itemActivated", (event: ItemActivatedEvent) => {
|
160
|
+
# event.item // Element: the <li> item that was activated
|
161
|
+
# event.checked // boolean: whether or not the result of the activation checked the item
|
162
|
+
# })
|
163
|
+
# ```
|
131
164
|
class ActionMenu < Primer::Component
|
132
165
|
status :alpha
|
133
166
|
|
@@ -175,7 +208,7 @@ module Primer
|
|
175
208
|
|
176
209
|
@system_arguments[:preload] = true if @src.present? && preload?
|
177
210
|
|
178
|
-
select_variant = fetch_or_fallback(SELECT_VARIANT_OPTIONS, select_variant, DEFAULT_SELECT_VARIANT)
|
211
|
+
@select_variant = fetch_or_fallback(SELECT_VARIANT_OPTIONS, select_variant, DEFAULT_SELECT_VARIANT)
|
179
212
|
|
180
213
|
@system_arguments[:tag] = :"action-menu"
|
181
214
|
@system_arguments[:"data-select-variant"] = select_variant
|
@@ -243,6 +276,10 @@ module Primer
|
|
243
276
|
@list.with_avatar_item(**system_arguments, &block)
|
244
277
|
end
|
245
278
|
|
279
|
+
def with_group(**system_arguments, &block)
|
280
|
+
@list.with_group(**system_arguments, &block)
|
281
|
+
end
|
282
|
+
|
246
283
|
private
|
247
284
|
|
248
285
|
def before_render
|
@@ -1 +1 @@
|
|
1
|
-
anchored-position[popover]{
|
1
|
+
anchored-position[popover]{border-width:0;min-width:192px;overflow:visible;padding:0;position:absolute}anchored-position:not(.Overlay){background:none}.Overlay[popover]:not(:popover-open){display:none}anchored-position.not-anchored::backdrop{background-color:var(--overlay-backdrop-bgColor,var(--color-neutral-muted))}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["overlay.pcss"],"names":[],"mappings":"AAAA,
|
1
|
+
{"version":3,"sources":["overlay.pcss"],"names":[],"mappings":"AAAA,2BACE,cAAe,CAGf,eAAgB,CAChB,gBAAiB,CAHjB,SAAU,CACV,iBAGF,CAEA,gCACE,eACF,CAGA,qCACE,YACF,CAEA,yCACE,2EACF","file":"overlay.css","sourcesContent":["anchored-position[popover] {\n border-width: 0;\n padding: 0;\n position: absolute;\n min-width: 192px;\n overflow: visible;\n}\n\nanchored-position:not(.Overlay) {\n background: none;\n}\n\n/* stylelint-disable-next-line selector-pseudo-class-no-unknown */\n.Overlay[popover]:not(:popover-open) {\n display: none\n}\n\nanchored-position.not-anchored::backdrop {\n background-color: var(--overlay-backdrop-bgColor, var(--color-neutral-muted));\n}\n"]}
|
@@ -4,11 +4,15 @@ anchored-position[popover] {
|
|
4
4
|
position: absolute;
|
5
5
|
min-width: 192px;
|
6
6
|
overflow: visible;
|
7
|
+
}
|
8
|
+
|
9
|
+
anchored-position:not(.Overlay) {
|
7
10
|
background: none;
|
8
11
|
}
|
9
12
|
|
10
|
-
|
11
|
-
|
13
|
+
/* stylelint-disable-next-line selector-pseudo-class-no-unknown */
|
14
|
+
.Overlay[popover]:not(:popover-open) {
|
15
|
+
display: none
|
12
16
|
}
|
13
17
|
|
14
18
|
anchored-position.not-anchored::backdrop {
|