primer_view_components 0.13.2 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +63 -0
  3. data/app/assets/javascripts/app/components/primer/alpha/action_menu/action_menu_element.d.ts +20 -0
  4. data/app/assets/javascripts/app/components/primer/beta/nav_list.d.ts +0 -11
  5. data/app/assets/javascripts/app/components/primer/beta/nav_list_group_element.d.ts +19 -0
  6. data/app/assets/javascripts/app/components/primer/primer.d.ts +1 -0
  7. data/app/assets/javascripts/primer_view_components.js +1 -1
  8. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  9. data/app/assets/styles/primer_view_components.css +1 -1
  10. data/app/assets/styles/primer_view_components.css.map +1 -1
  11. data/app/components/primer/alpha/action_bar.css +1 -1
  12. data/app/components/primer/alpha/action_bar.css.json +1 -4
  13. data/app/components/primer/alpha/action_bar.css.map +1 -1
  14. data/app/components/primer/alpha/action_bar.pcss +1 -17
  15. data/app/components/primer/alpha/action_bar_element.js +21 -9
  16. data/app/components/primer/alpha/action_list/item.rb +13 -3
  17. data/app/components/primer/alpha/action_list.css +1 -1
  18. data/app/components/primer/alpha/action_list.css.json +1 -0
  19. data/app/components/primer/alpha/action_list.css.map +1 -1
  20. data/app/components/primer/alpha/action_list.pcss +3 -1
  21. data/app/components/primer/alpha/action_list.rb +5 -5
  22. data/app/components/primer/alpha/action_menu/action_menu_element.d.ts +20 -0
  23. data/app/components/primer/alpha/action_menu/action_menu_element.js +126 -28
  24. data/app/components/primer/alpha/action_menu/action_menu_element.ts +110 -12
  25. data/app/components/primer/alpha/action_menu/group.rb +23 -0
  26. data/app/components/primer/alpha/action_menu/heading.rb +17 -0
  27. data/app/components/primer/alpha/action_menu/list.html.erb +1 -0
  28. data/app/components/primer/alpha/action_menu/list.rb +62 -60
  29. data/app/components/primer/alpha/action_menu/list_wrapper.rb +40 -0
  30. data/app/components/primer/alpha/action_menu.html.erb +20 -18
  31. data/app/components/primer/alpha/action_menu.rb +38 -1
  32. data/app/components/primer/alpha/auto_complete.css +1 -1
  33. data/app/components/primer/alpha/auto_complete.css.map +1 -1
  34. data/app/components/primer/alpha/button_marketing.css +1 -1
  35. data/app/components/primer/alpha/button_marketing.css.map +1 -1
  36. data/app/components/primer/alpha/dialog/header.rb +1 -1
  37. data/app/components/primer/alpha/dropdown.css +1 -1
  38. data/app/components/primer/alpha/dropdown.css.map +1 -1
  39. data/app/components/primer/alpha/layout.css +1 -1
  40. data/app/components/primer/alpha/layout.css.map +1 -1
  41. data/app/components/primer/alpha/menu.css +1 -1
  42. data/app/components/primer/alpha/menu.css.map +1 -1
  43. data/app/components/primer/alpha/nav_list/divider.rb +1 -0
  44. data/app/components/primer/alpha/nav_list/group.rb +1 -0
  45. data/app/components/primer/alpha/nav_list/heading.rb +1 -0
  46. data/app/components/primer/alpha/nav_list/item.rb +1 -0
  47. data/app/components/primer/alpha/nav_list.rb +1 -0
  48. data/app/components/primer/alpha/octicon_symbols.html.erb +1 -1
  49. data/app/components/primer/alpha/overlay.css +1 -1
  50. data/app/components/primer/alpha/overlay.css.json +2 -2
  51. data/app/components/primer/alpha/overlay.css.map +1 -1
  52. data/app/components/primer/alpha/overlay.pcss +6 -2
  53. data/app/components/primer/alpha/segmented_control.css +1 -1
  54. data/app/components/primer/alpha/segmented_control.css.json +0 -1
  55. data/app/components/primer/alpha/segmented_control.css.map +1 -1
  56. data/app/components/primer/alpha/segmented_control.js +2 -1
  57. data/app/components/primer/alpha/segmented_control.pcss +0 -4
  58. data/app/components/primer/alpha/tab_nav.css +1 -1
  59. data/app/components/primer/alpha/tab_nav.css.map +1 -1
  60. data/app/components/primer/alpha/text_field.css +1 -1
  61. data/app/components/primer/alpha/text_field.css.json +11 -5
  62. data/app/components/primer/alpha/text_field.css.map +1 -1
  63. data/app/components/primer/alpha/text_field.pcss +10 -1
  64. data/app/components/primer/alpha/toggle_switch.css +1 -1
  65. data/app/components/primer/alpha/toggle_switch.css.map +1 -1
  66. data/app/components/primer/alpha/tool_tip.js +8 -4
  67. data/app/components/primer/alpha/tool_tip.ts +9 -4
  68. data/app/components/primer/alpha/underline_nav.css +1 -1
  69. data/app/components/primer/alpha/underline_nav.css.map +1 -1
  70. data/app/components/primer/alpha/x_banner.js +2 -1
  71. data/app/components/primer/anchored_position.js +2 -1
  72. data/app/components/primer/beta/auto_complete/auto_complete.html.erb +1 -7
  73. data/app/components/primer/beta/auto_complete/item.rb +2 -2
  74. data/app/components/primer/beta/auto_complete.rb +6 -1
  75. data/app/components/primer/beta/avatar.rb +1 -1
  76. data/app/components/primer/beta/base_button.rb +3 -4
  77. data/app/components/primer/beta/blankslate.css +1 -1
  78. data/app/components/primer/beta/blankslate.css.json +1 -0
  79. data/app/components/primer/beta/blankslate.css.map +1 -1
  80. data/app/components/primer/beta/blankslate.html.erb +16 -14
  81. data/app/components/primer/beta/blankslate.pcss +52 -2
  82. data/app/components/primer/beta/border_box.css +1 -1
  83. data/app/components/primer/beta/border_box.css.map +1 -1
  84. data/app/components/primer/beta/button.css +1 -1
  85. data/app/components/primer/beta/button.css.json +9 -9
  86. data/app/components/primer/beta/button.css.map +1 -1
  87. data/app/components/primer/beta/button.html.erb +18 -20
  88. data/app/components/primer/beta/button.pcss +15 -16
  89. data/app/components/primer/beta/button.rb +3 -0
  90. data/app/components/primer/beta/button_group.css +1 -1
  91. data/app/components/primer/beta/button_group.css.json +2 -4
  92. data/app/components/primer/beta/button_group.css.map +1 -1
  93. data/app/components/primer/beta/button_group.html.erb +3 -1
  94. data/app/components/primer/beta/button_group.pcss +2 -4
  95. data/app/components/primer/beta/button_group.rb +41 -12
  96. data/app/components/primer/beta/clipboard_copy.rb +4 -0
  97. data/app/components/primer/beta/clipboard_copy_button.rb +25 -0
  98. data/app/components/primer/beta/counter.rb +1 -1
  99. data/app/components/primer/beta/flash.html.erb +1 -1
  100. data/app/components/primer/beta/icon_button.html.erb +4 -6
  101. data/app/components/primer/beta/icon_button.rb +1 -3
  102. data/app/components/primer/beta/label.css +1 -1
  103. data/app/components/primer/beta/label.css.map +1 -1
  104. data/app/components/primer/beta/link.css +1 -1
  105. data/app/components/primer/beta/link.css.map +1 -1
  106. data/app/components/primer/beta/nav_list/group.html.erb +7 -5
  107. data/app/components/primer/beta/nav_list/group.rb +2 -2
  108. data/app/components/primer/beta/nav_list.d.ts +0 -11
  109. data/app/components/primer/beta/nav_list.js +14 -90
  110. data/app/components/primer/beta/nav_list.ts +1 -85
  111. data/app/components/primer/beta/nav_list_group_element.d.ts +19 -0
  112. data/app/components/primer/beta/nav_list_group_element.js +108 -0
  113. data/app/components/primer/beta/nav_list_group_element.ts +97 -0
  114. data/app/components/primer/beta/relative_time.rb +4 -4
  115. data/app/components/primer/beta/subhead.css +1 -1
  116. data/app/components/primer/beta/subhead.css.json +2 -0
  117. data/app/components/primer/beta/subhead.css.map +1 -1
  118. data/app/components/primer/beta/subhead.pcss +8 -1
  119. data/app/components/primer/beta/subhead.rb +9 -1
  120. data/app/components/primer/component.rb +3 -0
  121. data/app/components/primer/focus_group.js +2 -1
  122. data/app/components/primer/primer.d.ts +1 -0
  123. data/app/components/primer/primer.js +1 -0
  124. data/app/components/primer/primer.ts +1 -0
  125. data/app/forms/action_menu_form.rb +20 -0
  126. data/app/forms/immediate_validation_form.rb +2 -2
  127. data/app/lib/primer/experimental_render_helpers.rb +32 -0
  128. data/app/lib/primer/experimental_slot_helpers.rb +30 -0
  129. data/app/lib/primer/fetch_or_fallback_helper.rb +0 -1
  130. data/app/lib/primer/octicon/cache.rb +1 -1
  131. data/lib/primer/classify.rb +0 -2
  132. data/lib/primer/forms/action_menu.html.erb +6 -0
  133. data/lib/primer/forms/action_menu.rb +25 -0
  134. data/lib/primer/forms/acts_as_component.rb +0 -3
  135. data/lib/primer/forms/base.rb +0 -1
  136. data/lib/primer/forms/base_component.rb +0 -2
  137. data/lib/primer/forms/dsl/action_menu_input.rb +36 -0
  138. data/lib/primer/forms/dsl/input.rb +8 -1
  139. data/lib/primer/forms/dsl/input_methods.rb +9 -0
  140. data/lib/primer/forms/dsl/text_field_input.rb +8 -0
  141. data/lib/primer/forms/primer_base_component_wrapper.rb +0 -2
  142. data/lib/primer/forms/primer_text_field.js +40 -5
  143. data/lib/primer/forms/primer_text_field.ts +39 -7
  144. data/lib/primer/forms/validation_message.html.erb +2 -1
  145. data/lib/primer/static/generate_info_arch.rb +1 -2
  146. data/lib/primer/static/generate_previews.rb +0 -2
  147. data/lib/primer/view_components/engine.rb +5 -1
  148. data/lib/primer/view_components/linters/base_linter.rb +3 -2
  149. data/lib/primer/view_components/linters/deprecated_components_counter.rb +1 -1
  150. data/lib/primer/view_components/linters/disallow_action_list.rb +1 -0
  151. data/lib/primer/view_components/linters/severity_schema.rb +1 -0
  152. data/lib/primer/view_components/version.rb +2 -3
  153. data/lib/primer/yard/lookbook_pages_backend.rb +2 -4
  154. data/lib/rubocop/cop/primer/base_cop.rb +1 -1
  155. data/lib/rubocop/cop/primer/deprecated_arguments.rb +0 -2
  156. data/lib/rubocop/cop/primer/deprecated_components.rb +1 -1
  157. data/lib/rubocop/cop/primer/deprecated_label_schemes.rb +1 -1
  158. data/lib/rubocop/cop/primer/deprecated_label_variants.rb +1 -1
  159. data/previews/primer/alpha/action_menu_preview.rb +73 -7
  160. data/previews/primer/alpha/dialog_preview/with_auto_complete.html.erb +8 -0
  161. data/previews/primer/alpha/dialog_preview.rb +17 -0
  162. data/previews/primer/alpha/octicon_symbols_preview/default.html.erb +6 -0
  163. data/previews/primer/alpha/octicon_symbols_preview/playground.html.erb +13 -0
  164. data/previews/primer/alpha/octicon_symbols_preview.rb +21 -0
  165. data/previews/primer/alpha/text_field_preview.rb +5 -0
  166. data/previews/primer/beta/avatar_preview.rb +6 -0
  167. data/previews/primer/beta/blankslate_preview/inside_flex_container.html.erb +6 -0
  168. data/previews/primer/beta/blankslate_preview.rb +3 -0
  169. data/previews/primer/beta/button_group_preview.rb +11 -0
  170. data/previews/primer/beta/clipboard_copy_button_preview.rb +29 -0
  171. data/previews/primer/beta/nav_list_preview.rb +10 -1
  172. data/previews/primer/beta/subhead_preview.rb +32 -4
  173. data/previews/primer/forms_preview/action_menu_form.html.erb +3 -0
  174. data/previews/primer/forms_preview.rb +4 -0
  175. data/static/arguments.json +167 -14
  176. data/static/audited_at.json +6 -1
  177. data/static/classes.json +12 -3
  178. data/static/constants.json +25 -1
  179. data/static/info_arch.json +585 -78
  180. data/static/previews.json +198 -0
  181. data/static/statuses.json +5 -0
  182. metadata +24 -2
@@ -15,7 +15,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
15
15
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
16
16
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
17
17
  };
18
- var _ActionMenuElement_instances, _ActionMenuElement_abortController, _ActionMenuElement_originalLabel, _ActionMenuElement_inputName, _ActionMenuElement_invokerBeingClicked, _ActionMenuElement_softDisableItems, _ActionMenuElement_potentiallyDisallowActivation, _ActionMenuElement_isKeyboardActivation, _ActionMenuElement_isKeyboardActivationViaEnter, _ActionMenuElement_isKeyboardActivationViaSpace, _ActionMenuElement_isMouseActivation, _ActionMenuElement_isActivation, _ActionMenuElement_handleInvokerActivated, _ActionMenuElement_handleDialogItemActivated, _ActionMenuElement_handleItemActivated, _ActionMenuElement_activateItem, _ActionMenuElement_handleIncludeFragmentReplaced, _ActionMenuElement_handleFocusOut, _ActionMenuElement_show, _ActionMenuElement_hide, _ActionMenuElement_isOpen, _ActionMenuElement_setDynamicLabel, _ActionMenuElement_updateInput, _ActionMenuElement_firstItem_get, _ActionMenuElement_items_get;
18
+ var _ActionMenuElement_instances, _ActionMenuElement_abortController, _ActionMenuElement_originalLabel, _ActionMenuElement_inputName, _ActionMenuElement_invokerBeingClicked, _ActionMenuElement_softDisableItems, _ActionMenuElement_potentiallyDisallowActivation, _ActionMenuElement_isKeyboardActivation, _ActionMenuElement_isKeyboardActivationViaEnter, _ActionMenuElement_isKeyboardActivationViaSpace, _ActionMenuElement_isMouseActivation, _ActionMenuElement_isActivation, _ActionMenuElement_handleInvokerActivated, _ActionMenuElement_handleDialogItemActivated, _ActionMenuElement_handleItemActivated, _ActionMenuElement_activateItem, _ActionMenuElement_handleIncludeFragmentReplaced, _ActionMenuElement_handleFocusOut, _ActionMenuElement_show, _ActionMenuElement_hide, _ActionMenuElement_isOpen, _ActionMenuElement_setDynamicLabel, _ActionMenuElement_updateInput, _ActionMenuElement_firstItem_get;
19
19
  import { controller, target } from '@github/catalyst';
20
20
  import '@oddbird/popover-polyfill';
21
21
  const validSelectors = ['[role="menuitem"]', '[role="menuitemcheckbox"]', '[role="menuitemradio"]'];
@@ -84,7 +84,7 @@ let ActionMenuElement = class ActionMenuElement extends HTMLElement {
84
84
  results.push({
85
85
  label: labelEl === null || labelEl === void 0 ? void 0 : labelEl.textContent,
86
86
  value: selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.getAttribute('data-value'),
87
- element: selectedItem
87
+ element: selectedItem,
88
88
  });
89
89
  }
90
90
  return results;
@@ -101,7 +101,7 @@ let ActionMenuElement = class ActionMenuElement extends HTMLElement {
101
101
  __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_softDisableItems).call(this);
102
102
  if (this.includeFragment) {
103
103
  this.includeFragment.addEventListener('include-fragment-replaced', this, {
104
- signal
104
+ signal,
105
105
  });
106
106
  }
107
107
  }
@@ -141,6 +141,8 @@ let ActionMenuElement = class ActionMenuElement extends HTMLElement {
141
141
  const item = event.target.closest(menuItemSelectors.join(','));
142
142
  const targetIsItem = item !== null;
143
143
  if (targetIsItem && eventIsActivation) {
144
+ if (__classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_potentiallyDisallowActivation).call(this, event))
145
+ return;
144
146
  const dialogInvoker = item.closest('[data-show-dialog-id]');
145
147
  if (dialogInvoker) {
146
148
  const dialog = this.ownerDocument.getElementById(dialogInvoker.getAttribute('data-show-dialog-id') || '');
@@ -150,7 +152,7 @@ let ActionMenuElement = class ActionMenuElement extends HTMLElement {
150
152
  }
151
153
  }
152
154
  __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_activateItem).call(this, event, item);
153
- __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_handleItemActivated).call(this, event, item);
155
+ __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_handleItemActivated).call(this, item);
154
156
  // Pressing the space key on a button or link will cause the page to scroll unless preventDefault()
155
157
  // is called. While calling preventDefault() appears to have no effect on link navigation, it skips
156
158
  // form submission. The code below therefore only calls preventDefault() if the button has been
@@ -167,41 +169,125 @@ let ActionMenuElement = class ActionMenuElement extends HTMLElement {
167
169
  __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_handleIncludeFragmentReplaced).call(this);
168
170
  }
169
171
  }
172
+ get items() {
173
+ return Array.from(this.querySelectorAll(menuItemSelectors.join(',')));
174
+ }
175
+ getItemById(itemId) {
176
+ return this.querySelector(`li[data-item-id="${itemId}"`);
177
+ }
178
+ isItemDisabled(item) {
179
+ if (item) {
180
+ return item.classList.contains('ActionListItem--disabled');
181
+ }
182
+ else {
183
+ return false;
184
+ }
185
+ }
186
+ disableItem(item) {
187
+ if (item) {
188
+ item.classList.add('ActionListItem--disabled');
189
+ item.querySelector('.ActionListContent').setAttribute('aria-disabled', 'true');
190
+ }
191
+ }
192
+ enableItem(item) {
193
+ if (item) {
194
+ item.classList.remove('ActionListItem--disabled');
195
+ item.querySelector('.ActionListContent').removeAttribute('aria-disabled');
196
+ }
197
+ }
198
+ isItemHidden(item) {
199
+ if (item) {
200
+ return item.hasAttribute('hidden');
201
+ }
202
+ else {
203
+ return false;
204
+ }
205
+ }
206
+ hideItem(item) {
207
+ if (item) {
208
+ item.setAttribute('hidden', 'hidden');
209
+ }
210
+ }
211
+ showItem(item) {
212
+ if (item) {
213
+ item.removeAttribute('hidden');
214
+ }
215
+ }
216
+ isItemChecked(item) {
217
+ if (item) {
218
+ return item.querySelector('.ActionListContent').getAttribute('aria-checked') === 'true';
219
+ }
220
+ else {
221
+ return false;
222
+ }
223
+ }
224
+ checkItem(item) {
225
+ if (item && (this.selectVariant === 'single' || this.selectVariant === 'multiple')) {
226
+ const itemContent = item.querySelector('.ActionListContent');
227
+ const ariaChecked = itemContent.getAttribute('aria-checked') === 'true';
228
+ if (!ariaChecked) {
229
+ __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_handleItemActivated).call(this, itemContent);
230
+ }
231
+ }
232
+ }
233
+ uncheckItem(item) {
234
+ if (item && (this.selectVariant === 'single' || this.selectVariant === 'multiple')) {
235
+ const itemContent = item.querySelector('.ActionListContent');
236
+ const ariaChecked = itemContent.getAttribute('aria-checked') === 'true';
237
+ if (ariaChecked) {
238
+ __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_handleItemActivated).call(this, itemContent);
239
+ }
240
+ }
241
+ }
170
242
  };
171
- _ActionMenuElement_abortController = new WeakMap(), _ActionMenuElement_originalLabel = new WeakMap(), _ActionMenuElement_inputName = new WeakMap(), _ActionMenuElement_invokerBeingClicked = new WeakMap(), _ActionMenuElement_instances = new WeakSet(), _ActionMenuElement_softDisableItems = function _ActionMenuElement_softDisableItems() {
243
+ _ActionMenuElement_abortController = new WeakMap();
244
+ _ActionMenuElement_originalLabel = new WeakMap();
245
+ _ActionMenuElement_inputName = new WeakMap();
246
+ _ActionMenuElement_invokerBeingClicked = new WeakMap();
247
+ _ActionMenuElement_instances = new WeakSet();
248
+ _ActionMenuElement_softDisableItems = function _ActionMenuElement_softDisableItems() {
172
249
  const { signal } = __classPrivateFieldGet(this, _ActionMenuElement_abortController, "f");
173
- for (const item of __classPrivateFieldGet(this, _ActionMenuElement_instances, "a", _ActionMenuElement_items_get)) {
250
+ for (const item of this.querySelectorAll(validSelectors.join(','))) {
174
251
  item.addEventListener('click', __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_potentiallyDisallowActivation).bind(this), { signal });
175
252
  item.addEventListener('keydown', __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_potentiallyDisallowActivation).bind(this), { signal });
176
253
  }
177
- }, _ActionMenuElement_potentiallyDisallowActivation = function _ActionMenuElement_potentiallyDisallowActivation(event) {
254
+ };
255
+ _ActionMenuElement_potentiallyDisallowActivation = function _ActionMenuElement_potentiallyDisallowActivation(event) {
178
256
  if (!__classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isActivation).call(this, event))
179
- return;
257
+ return false;
180
258
  const item = event.target.closest(menuItemSelectors.join(','));
181
259
  if (!item)
182
- return;
260
+ return false;
183
261
  if (item.getAttribute('aria-disabled')) {
184
262
  event.preventDefault();
185
263
  event.stopPropagation();
186
264
  event.stopImmediatePropagation();
265
+ return true;
187
266
  }
188
- }, _ActionMenuElement_isKeyboardActivation = function _ActionMenuElement_isKeyboardActivation(event) {
267
+ return false;
268
+ };
269
+ _ActionMenuElement_isKeyboardActivation = function _ActionMenuElement_isKeyboardActivation(event) {
189
270
  return __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isKeyboardActivationViaEnter).call(this, event) || __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isKeyboardActivationViaSpace).call(this, event);
190
- }, _ActionMenuElement_isKeyboardActivationViaEnter = function _ActionMenuElement_isKeyboardActivationViaEnter(event) {
271
+ };
272
+ _ActionMenuElement_isKeyboardActivationViaEnter = function _ActionMenuElement_isKeyboardActivationViaEnter(event) {
191
273
  return (event instanceof KeyboardEvent &&
192
274
  event.type === 'keydown' &&
193
275
  !(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
194
276
  event.key === 'Enter');
195
- }, _ActionMenuElement_isKeyboardActivationViaSpace = function _ActionMenuElement_isKeyboardActivationViaSpace(event) {
277
+ };
278
+ _ActionMenuElement_isKeyboardActivationViaSpace = function _ActionMenuElement_isKeyboardActivationViaSpace(event) {
196
279
  return (event instanceof KeyboardEvent &&
197
280
  event.type === 'keydown' &&
198
281
  !(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
199
282
  event.key === ' ');
200
- }, _ActionMenuElement_isMouseActivation = function _ActionMenuElement_isMouseActivation(event) {
283
+ };
284
+ _ActionMenuElement_isMouseActivation = function _ActionMenuElement_isMouseActivation(event) {
201
285
  return event instanceof MouseEvent && event.type === 'click';
202
- }, _ActionMenuElement_isActivation = function _ActionMenuElement_isActivation(event) {
286
+ };
287
+ _ActionMenuElement_isActivation = function _ActionMenuElement_isActivation(event) {
203
288
  return __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isMouseActivation).call(this, event) || __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isKeyboardActivation).call(this, event);
204
- }, _ActionMenuElement_handleInvokerActivated = function _ActionMenuElement_handleInvokerActivated(event) {
289
+ };
290
+ _ActionMenuElement_handleInvokerActivated = function _ActionMenuElement_handleInvokerActivated(event) {
205
291
  var _a;
206
292
  event.preventDefault();
207
293
  event.stopPropagation();
@@ -212,7 +298,8 @@ _ActionMenuElement_abortController = new WeakMap(), _ActionMenuElement_originalL
212
298
  __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_show).call(this);
213
299
  (_a = __classPrivateFieldGet(this, _ActionMenuElement_instances, "a", _ActionMenuElement_firstItem_get)) === null || _a === void 0 ? void 0 : _a.focus();
214
300
  }
215
- }, _ActionMenuElement_handleDialogItemActivated = function _ActionMenuElement_handleDialogItemActivated(event, dialog) {
301
+ };
302
+ _ActionMenuElement_handleDialogItemActivated = function _ActionMenuElement_handleDialogItemActivated(event, dialog) {
216
303
  this.querySelector('.ActionListWrap').style.display = 'none';
217
304
  const dialog_controller = new AbortController();
218
305
  const { signal } = dialog_controller;
@@ -225,7 +312,8 @@ _ActionMenuElement_abortController = new WeakMap(), _ActionMenuElement_originalL
225
312
  };
226
313
  dialog.addEventListener('close', handleDialogClose, { signal });
227
314
  dialog.addEventListener('cancel', handleDialogClose, { signal });
228
- }, _ActionMenuElement_handleItemActivated = function _ActionMenuElement_handleItemActivated(event, item) {
315
+ };
316
+ _ActionMenuElement_handleItemActivated = function _ActionMenuElement_handleItemActivated(item) {
229
317
  // Hide popover after current event loop to prevent changes in focus from
230
318
  // altering the target of the event. Not doing this specifically affects
231
319
  // <a> tags. It causes the event to be sent to the currently focused element
@@ -262,7 +350,11 @@ _ActionMenuElement_abortController = new WeakMap(), _ActionMenuElement_originalL
262
350
  item.setAttribute('aria-checked', `${checked}`);
263
351
  }
264
352
  __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_updateInput).call(this);
265
- }, _ActionMenuElement_activateItem = function _ActionMenuElement_activateItem(event, item) {
353
+ this.dispatchEvent(new CustomEvent('itemActivated', {
354
+ detail: { item: item.parentElement, checked: this.isItemChecked(item.parentElement) },
355
+ }));
356
+ };
357
+ _ActionMenuElement_activateItem = function _ActionMenuElement_activateItem(event, item) {
266
358
  const eventWillActivateByDefault = (event instanceof MouseEvent && event.type === 'click') ||
267
359
  (event instanceof KeyboardEvent &&
268
360
  event.type === 'keydown' &&
@@ -277,22 +369,28 @@ _ActionMenuElement_abortController = new WeakMap(), _ActionMenuElement_originalL
277
369
  event.stopPropagation();
278
370
  const elem = item;
279
371
  elem.click();
280
- }, _ActionMenuElement_handleIncludeFragmentReplaced = function _ActionMenuElement_handleIncludeFragmentReplaced() {
372
+ };
373
+ _ActionMenuElement_handleIncludeFragmentReplaced = function _ActionMenuElement_handleIncludeFragmentReplaced() {
281
374
  if (__classPrivateFieldGet(this, _ActionMenuElement_instances, "a", _ActionMenuElement_firstItem_get))
282
375
  __classPrivateFieldGet(this, _ActionMenuElement_instances, "a", _ActionMenuElement_firstItem_get).focus();
283
376
  __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_softDisableItems).call(this);
284
- }, _ActionMenuElement_handleFocusOut = function _ActionMenuElement_handleFocusOut() {
377
+ };
378
+ _ActionMenuElement_handleFocusOut = function _ActionMenuElement_handleFocusOut() {
285
379
  __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_hide).call(this);
286
- }, _ActionMenuElement_show = function _ActionMenuElement_show() {
380
+ };
381
+ _ActionMenuElement_show = function _ActionMenuElement_show() {
287
382
  var _a;
288
383
  (_a = this.popoverElement) === null || _a === void 0 ? void 0 : _a.showPopover();
289
- }, _ActionMenuElement_hide = function _ActionMenuElement_hide() {
384
+ };
385
+ _ActionMenuElement_hide = function _ActionMenuElement_hide() {
290
386
  var _a;
291
387
  (_a = this.popoverElement) === null || _a === void 0 ? void 0 : _a.hidePopover();
292
- }, _ActionMenuElement_isOpen = function _ActionMenuElement_isOpen() {
388
+ };
389
+ _ActionMenuElement_isOpen = function _ActionMenuElement_isOpen() {
293
390
  var _a;
294
391
  return (_a = this.popoverElement) === null || _a === void 0 ? void 0 : _a.matches(':popover-open');
295
- }, _ActionMenuElement_setDynamicLabel = function _ActionMenuElement_setDynamicLabel() {
392
+ };
393
+ _ActionMenuElement_setDynamicLabel = function _ActionMenuElement_setDynamicLabel() {
296
394
  if (!this.dynamicLabel)
297
395
  return;
298
396
  const invokerLabel = this.invokerLabel;
@@ -311,7 +409,8 @@ _ActionMenuElement_abortController = new WeakMap(), _ActionMenuElement_originalL
311
409
  else {
312
410
  invokerLabel.textContent = __classPrivateFieldGet(this, _ActionMenuElement_originalLabel, "f");
313
411
  }
314
- }, _ActionMenuElement_updateInput = function _ActionMenuElement_updateInput() {
412
+ };
413
+ _ActionMenuElement_updateInput = function _ActionMenuElement_updateInput() {
315
414
  if (this.selectVariant === 'single') {
316
415
  const input = this.querySelector(`[data-list-inputs=true] input`);
317
416
  if (!input)
@@ -347,10 +446,9 @@ _ActionMenuElement_abortController = new WeakMap(), _ActionMenuElement_originalL
347
446
  input.remove();
348
447
  }
349
448
  }
350
- }, _ActionMenuElement_firstItem_get = function _ActionMenuElement_firstItem_get() {
449
+ };
450
+ _ActionMenuElement_firstItem_get = function _ActionMenuElement_firstItem_get() {
351
451
  return this.querySelector(menuItemSelectors.join(','));
352
- }, _ActionMenuElement_items_get = function _ActionMenuElement_items_get() {
353
- return Array.from(this.querySelectorAll(menuItemSelectors.join(',')));
354
452
  };
355
453
  __decorate([
356
454
  target
@@ -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.#items) {
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
- #potentiallyDisallowActivation(event: Event) {
120
- if (!this.#isActivation(event)) return
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
- disconnectedCallback() {
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(event, item)
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(event: Event, item: Element) {
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 #items(): HTMLElement[] {
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) %>