primer_view_components 0.10.0 → 0.11.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/components/primer/alpha/action_bar/item.rb +7 -4
- data/app/components/primer/alpha/action_bar.rb +2 -2
- data/app/components/primer/alpha/action_bar_element.js +2 -2
- data/app/components/primer/alpha/action_bar_element.ts +2 -2
- data/app/components/primer/alpha/action_menu/action_menu_element.js +168 -84
- data/app/components/primer/alpha/action_menu/action_menu_element.ts +204 -79
- data/app/components/primer/alpha/action_menu/list.rb +0 -2
- data/app/components/primer/focus_group.js +28 -3
- data/app/components/primer/focus_group.ts +27 -1
- data/lib/primer/view_components/version.rb +1 -1
- data/previews/primer/alpha/action_menu_preview/with_actions.html.erb +5 -4
- data/previews/primer/alpha/action_menu_preview.rb +4 -1
- data/static/arguments.json +12 -1
- data/static/info_arch.json +12 -1
- metadata +2 -2
@@ -20,6 +20,7 @@ export class ActionMenuElement extends HTMLElement {
|
|
20
20
|
#abortController: AbortController
|
21
21
|
#originalLabel = ''
|
22
22
|
#inputName = ''
|
23
|
+
#invokerBeingClicked = false
|
23
24
|
|
24
25
|
get selectVariant(): SelectVariant {
|
25
26
|
return this.getAttribute('data-select-variant') as SelectVariant
|
@@ -52,7 +53,7 @@ export class ActionMenuElement extends HTMLElement {
|
|
52
53
|
}
|
53
54
|
|
54
55
|
get popoverElement(): HTMLElement | null {
|
55
|
-
return this.invokerElement?.popoverTargetElement || null
|
56
|
+
return (this.invokerElement?.popoverTargetElement as HTMLElement) || null
|
56
57
|
}
|
57
58
|
|
58
59
|
get invokerElement(): HTMLButtonElement | null {
|
@@ -94,8 +95,10 @@ export class ActionMenuElement extends HTMLElement {
|
|
94
95
|
this.addEventListener('click', this, {signal})
|
95
96
|
this.addEventListener('mouseover', this, {signal})
|
96
97
|
this.addEventListener('focusout', this, {signal})
|
98
|
+
this.addEventListener('mousedown', this, {signal})
|
97
99
|
this.#setDynamicLabel()
|
98
100
|
this.#updateInput()
|
101
|
+
this.#softDisableItems()
|
99
102
|
|
100
103
|
if (this.includeFragment) {
|
101
104
|
this.includeFragment.addEventListener('include-fragment-replaced', this, {
|
@@ -104,19 +107,69 @@ export class ActionMenuElement extends HTMLElement {
|
|
104
107
|
}
|
105
108
|
}
|
106
109
|
|
110
|
+
#softDisableItems() {
|
111
|
+
const {signal} = this.#abortController
|
112
|
+
|
113
|
+
for (const item of this.#items) {
|
114
|
+
item.addEventListener('click', this.#potentiallyDisallowActivation.bind(this), {signal})
|
115
|
+
item.addEventListener('keydown', this.#potentiallyDisallowActivation.bind(this), {signal})
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
#potentiallyDisallowActivation(event: Event) {
|
120
|
+
if (!this.#isActivation(event)) return
|
121
|
+
|
122
|
+
const item = (event.target as HTMLElement).closest(menuItemSelectors.join(','))
|
123
|
+
if (!item) return
|
124
|
+
|
125
|
+
if (item.getAttribute('aria-disabled')) {
|
126
|
+
event.preventDefault()
|
127
|
+
event.stopPropagation()
|
128
|
+
event.stopImmediatePropagation()
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
107
132
|
disconnectedCallback() {
|
108
133
|
this.#abortController.abort()
|
109
134
|
}
|
110
135
|
|
136
|
+
#isKeyboardActivation(event: Event): boolean {
|
137
|
+
return (
|
138
|
+
event instanceof KeyboardEvent &&
|
139
|
+
event.type === 'keydown' &&
|
140
|
+
!(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
|
141
|
+
(event.key === 'Enter' || event.key === ' ')
|
142
|
+
)
|
143
|
+
}
|
144
|
+
|
145
|
+
#isMouseActivation(event: Event): boolean {
|
146
|
+
return event instanceof MouseEvent && event.type === 'click'
|
147
|
+
}
|
148
|
+
|
149
|
+
#isActivation(event: Event): boolean {
|
150
|
+
return this.#isMouseActivation(event) || this.#isKeyboardActivation(event)
|
151
|
+
}
|
152
|
+
|
111
153
|
handleEvent(event: Event) {
|
112
|
-
const
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
154
|
+
const targetIsInvoker = this.invokerElement?.contains(event.target as HTMLElement)
|
155
|
+
const eventIsActivation = this.#isActivation(event)
|
156
|
+
|
157
|
+
if (targetIsInvoker && event.type === 'mousedown') {
|
158
|
+
this.#invokerBeingClicked = true
|
159
|
+
return
|
160
|
+
}
|
161
|
+
|
162
|
+
// Prevent safari bug that dismisses menu on mousedown instead of allowing
|
163
|
+
// the click event to propagate to the button
|
164
|
+
if (event.type === 'mousedown') {
|
165
|
+
event.preventDefault()
|
166
|
+
return
|
167
|
+
}
|
168
|
+
|
169
|
+
if (targetIsInvoker && eventIsActivation) {
|
170
|
+
this.#handleInvokerActivated(event)
|
171
|
+
this.#invokerBeingClicked = false
|
172
|
+
return
|
120
173
|
}
|
121
174
|
|
122
175
|
// Ignore events within dialogs within menus
|
@@ -124,84 +177,161 @@ export class ActionMenuElement extends HTMLElement {
|
|
124
177
|
return
|
125
178
|
}
|
126
179
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
this.querySelector<HTMLElement>('.ActionListWrap')!.style.display = 'none'
|
135
|
-
const dialog_controller = new AbortController()
|
136
|
-
const {signal} = dialog_controller
|
137
|
-
const handleDialogClose = () => {
|
138
|
-
dialog_controller.abort()
|
139
|
-
this.querySelector<HTMLElement>('.ActionListWrap')!.style.display = ''
|
140
|
-
if (this.popoverElement?.matches(':popover-open')) {
|
141
|
-
this.popoverElement?.hidePopover()
|
142
|
-
}
|
180
|
+
if (event.type === 'focusout') {
|
181
|
+
if (this.#invokerBeingClicked) return
|
182
|
+
|
183
|
+
// Give the browser time to focus the next element
|
184
|
+
requestAnimationFrame(() => {
|
185
|
+
if (!this.contains(document.activeElement) || document.activeElement === this.invokerElement) {
|
186
|
+
this.#handleFocusOut()
|
143
187
|
}
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
}
|
188
|
+
})
|
189
|
+
|
190
|
+
return
|
148
191
|
}
|
149
192
|
|
150
|
-
|
193
|
+
const item = (event.target as Element).closest(menuItemSelectors.join(','))
|
194
|
+
const targetIsItem = item !== null
|
151
195
|
|
152
|
-
if (
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
setTimeout(() => {
|
163
|
-
if (this.popoverElement?.matches(':popover-open')) {
|
164
|
-
this.popoverElement?.hidePopover()
|
165
|
-
}
|
166
|
-
})
|
196
|
+
if (targetIsItem && eventIsActivation) {
|
197
|
+
const dialogInvoker = item.closest('[data-show-dialog-id]')
|
198
|
+
|
199
|
+
if (dialogInvoker) {
|
200
|
+
const dialog = this.ownerDocument.getElementById(dialogInvoker.getAttribute('data-show-dialog-id') || '')
|
201
|
+
|
202
|
+
if (dialog && this.contains(dialogInvoker) && this.contains(dialog)) {
|
203
|
+
this.#handleDialogItemActivated(event, dialog)
|
204
|
+
return
|
205
|
+
}
|
167
206
|
}
|
168
207
|
|
169
|
-
|
170
|
-
|
171
|
-
|
208
|
+
this.#activateItem(event, item)
|
209
|
+
this.#handleItemActivated(event, item)
|
210
|
+
return
|
211
|
+
}
|
172
212
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
213
|
+
if (event.type === 'include-fragment-replaced') {
|
214
|
+
this.#handleIncludeFragmentReplaced()
|
215
|
+
}
|
216
|
+
}
|
177
217
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
item.setAttribute('aria-checked', 'true')
|
182
|
-
}
|
218
|
+
#handleInvokerActivated(event: Event) {
|
219
|
+
event.preventDefault()
|
220
|
+
event.stopPropagation()
|
183
221
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
222
|
+
if (this.#isOpen()) {
|
223
|
+
this.#hide()
|
224
|
+
} else {
|
225
|
+
this.#show()
|
226
|
+
this.#firstItem?.focus()
|
227
|
+
}
|
228
|
+
}
|
189
229
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
230
|
+
#handleDialogItemActivated(event: Event, dialog: HTMLElement) {
|
231
|
+
this.querySelector<HTMLElement>('.ActionListWrap')!.style.display = 'none'
|
232
|
+
const dialog_controller = new AbortController()
|
233
|
+
const {signal} = dialog_controller
|
234
|
+
const handleDialogClose = () => {
|
235
|
+
dialog_controller.abort()
|
236
|
+
this.querySelector<HTMLElement>('.ActionListWrap')!.style.display = ''
|
237
|
+
if (this.#isOpen()) {
|
238
|
+
this.#hide()
|
194
239
|
}
|
240
|
+
}
|
241
|
+
dialog.addEventListener('close', handleDialogClose, {signal})
|
242
|
+
dialog.addEventListener('cancel', handleDialogClose, {signal})
|
243
|
+
}
|
244
|
+
|
245
|
+
#handleItemActivated(event: Event, item: Element) {
|
246
|
+
// Hide popover after current event loop to prevent changes in focus from
|
247
|
+
// altering the target of the event. Not doing this specifically affects
|
248
|
+
// <a> tags. It causes the event to be sent to the currently focused element
|
249
|
+
// instead of the anchor, which effectively prevents navigation, i.e. it
|
250
|
+
// appears as if hitting enter does nothing. Curiously, clicking instead
|
251
|
+
// works fine.
|
252
|
+
if (this.selectVariant !== 'multiple') {
|
253
|
+
setTimeout(() => {
|
254
|
+
if (this.#isOpen()) {
|
255
|
+
this.#hide()
|
256
|
+
}
|
257
|
+
})
|
258
|
+
}
|
259
|
+
|
260
|
+
// The rest of the code below deals with single/multiple selection behavior, and should not
|
261
|
+
// interfere with events fired by menu items whose behavior is specified outside the library.
|
262
|
+
if (this.selectVariant !== 'multiple' && this.selectVariant !== 'single') return
|
195
263
|
|
196
|
-
|
264
|
+
const ariaChecked = item.getAttribute('aria-checked')
|
265
|
+
const checked = ariaChecked !== 'true'
|
197
266
|
|
198
|
-
|
199
|
-
|
200
|
-
|
267
|
+
if (this.selectVariant === 'single') {
|
268
|
+
// Only check, never uncheck here. Single-select mode does not allow unchecking a checked item.
|
269
|
+
if (checked) {
|
270
|
+
item.setAttribute('aria-checked', 'true')
|
271
|
+
}
|
272
|
+
|
273
|
+
for (const checkedItem of this.querySelectorAll('[aria-checked]')) {
|
274
|
+
if (checkedItem !== item) {
|
275
|
+
checkedItem.setAttribute('aria-checked', 'false')
|
276
|
+
}
|
201
277
|
}
|
278
|
+
|
279
|
+
this.#setDynamicLabel()
|
280
|
+
} else {
|
281
|
+
// multi-select mode allows unchecking a checked item
|
282
|
+
item.setAttribute('aria-checked', `${checked}`)
|
283
|
+
}
|
284
|
+
|
285
|
+
this.#updateInput()
|
286
|
+
|
287
|
+
if (event instanceof KeyboardEvent && event.target instanceof HTMLButtonElement) {
|
288
|
+
// prevent buttons from being clicked twice
|
289
|
+
event.preventDefault()
|
290
|
+
return
|
202
291
|
}
|
203
292
|
}
|
204
293
|
|
294
|
+
#activateItem(event: Event, item: Element) {
|
295
|
+
const eventWillActivateByDefault =
|
296
|
+
(event instanceof MouseEvent && event.type === 'click') ||
|
297
|
+
(event instanceof KeyboardEvent &&
|
298
|
+
event.type === 'keydown' &&
|
299
|
+
!(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
|
300
|
+
event.key === 'Enter')
|
301
|
+
|
302
|
+
// if the event will result in activating the current item by default, i.e. is a
|
303
|
+
// mouse click or keyboard enter, bail out
|
304
|
+
if (eventWillActivateByDefault) return
|
305
|
+
|
306
|
+
// otherwise, event will not result in activation by default, so we stop it and
|
307
|
+
// simulate a click
|
308
|
+
event.stopPropagation()
|
309
|
+
const elem = item as HTMLElement
|
310
|
+
elem.click()
|
311
|
+
}
|
312
|
+
|
313
|
+
#handleIncludeFragmentReplaced() {
|
314
|
+
if (this.#firstItem) this.#firstItem.focus()
|
315
|
+
this.#softDisableItems()
|
316
|
+
}
|
317
|
+
|
318
|
+
// Close when focus leaves menu
|
319
|
+
#handleFocusOut() {
|
320
|
+
this.#hide()
|
321
|
+
}
|
322
|
+
|
323
|
+
#show() {
|
324
|
+
this.popoverElement?.showPopover()
|
325
|
+
}
|
326
|
+
|
327
|
+
#hide() {
|
328
|
+
this.popoverElement?.hidePopover()
|
329
|
+
}
|
330
|
+
|
331
|
+
#isOpen() {
|
332
|
+
return this.popoverElement?.matches(':popover-open')
|
333
|
+
}
|
334
|
+
|
205
335
|
#setDynamicLabel() {
|
206
336
|
if (!this.dynamicLabel) return
|
207
337
|
const invokerLabel = this.invokerLabel
|
@@ -261,18 +391,13 @@ export class ActionMenuElement extends HTMLElement {
|
|
261
391
|
}
|
262
392
|
}
|
263
393
|
|
264
|
-
#isActivationKeydown(event: Event): boolean {
|
265
|
-
return (
|
266
|
-
event instanceof KeyboardEvent &&
|
267
|
-
event.type === 'keydown' &&
|
268
|
-
!(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
|
269
|
-
(event.key === 'Enter' || event.key === ' ')
|
270
|
-
)
|
271
|
-
}
|
272
|
-
|
273
394
|
get #firstItem(): HTMLElement | null {
|
274
395
|
return this.querySelector(menuItemSelectors.join(','))
|
275
396
|
}
|
397
|
+
|
398
|
+
get #items(): HTMLElement[] {
|
399
|
+
return Array.from(this.querySelectorAll(menuItemSelectors.join(',')))
|
400
|
+
}
|
276
401
|
}
|
277
402
|
|
278
403
|
if (!window.customElements.get('action-menu')) {
|
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
9
9
|
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");
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
11
11
|
};
|
12
|
-
var _FocusGroupElement_instances, _FocusGroupElement_abortController, _FocusGroupElement_items_get;
|
12
|
+
var _FocusGroupElement_instances, _FocusGroupElement_retainSignal, _FocusGroupElement_abortController, _FocusGroupElement_items_get;
|
13
13
|
import '@oddbird/popover-polyfill';
|
14
14
|
const validSelectors = ['[role="menuitem"]', '[role="menuitemcheckbox"]', '[role="menuitemradio"]'];
|
15
15
|
const menuItemSelector = validSelectors.map(selector => `:not([hidden]) > ${selector}`).join(', ');
|
@@ -19,6 +19,7 @@ export default class FocusGroupElement extends HTMLElement {
|
|
19
19
|
constructor() {
|
20
20
|
super(...arguments);
|
21
21
|
_FocusGroupElement_instances.add(this);
|
22
|
+
_FocusGroupElement_retainSignal.set(this, null);
|
22
23
|
_FocusGroupElement_abortController.set(this, null);
|
23
24
|
}
|
24
25
|
get nowrap() {
|
@@ -57,11 +58,35 @@ export default class FocusGroupElement extends HTMLElement {
|
|
57
58
|
(_a = __classPrivateFieldGet(this, _FocusGroupElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
|
58
59
|
}
|
59
60
|
handleEvent(event) {
|
61
|
+
var _a;
|
60
62
|
const { direction, nowrap } = this;
|
61
63
|
if (event.type === 'focusin') {
|
62
64
|
if (this.retain && event.target instanceof Element && event.target.matches(menuItemSelector)) {
|
65
|
+
(_a = __classPrivateFieldGet(this, _FocusGroupElement_retainSignal, "f")) === null || _a === void 0 ? void 0 : _a.abort();
|
66
|
+
const { signal } = (__classPrivateFieldSet(this, _FocusGroupElement_retainSignal, new AbortController(), "f"));
|
63
67
|
for (const item of __classPrivateFieldGet(this, _FocusGroupElement_instances, "a", _FocusGroupElement_items_get)) {
|
64
68
|
item.setAttribute('tabindex', item === event.target ? '0' : '-1');
|
69
|
+
const popover = event.target.closest('[popover]');
|
70
|
+
if (item === event.target && (popover === null || popover === void 0 ? void 0 : popover.popover) === 'auto' && popover.closest('focus-group') === this) {
|
71
|
+
popover.addEventListener('toggle', (toggleEvent) => {
|
72
|
+
var _a, _b;
|
73
|
+
if (!(toggleEvent.target instanceof Element))
|
74
|
+
return;
|
75
|
+
if (toggleEvent.newState === 'closed') {
|
76
|
+
(_a = __classPrivateFieldGet(this, _FocusGroupElement_retainSignal, "f")) === null || _a === void 0 ? void 0 : _a.abort();
|
77
|
+
item.setAttribute('tabindex', '-1');
|
78
|
+
if (popover.id) {
|
79
|
+
const invoker = this.querySelector(`[popovertarget="${popover.id}"]`);
|
80
|
+
if (invoker) {
|
81
|
+
invoker.setAttribute('tabindex', '0');
|
82
|
+
}
|
83
|
+
else {
|
84
|
+
(_b = __classPrivateFieldGet(this, _FocusGroupElement_instances, "a", _FocusGroupElement_items_get)[0]) === null || _b === void 0 ? void 0 : _b.setAttribute('tabindex', '0');
|
85
|
+
}
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}, { signal });
|
89
|
+
}
|
65
90
|
}
|
66
91
|
}
|
67
92
|
}
|
@@ -121,7 +146,7 @@ export default class FocusGroupElement extends HTMLElement {
|
|
121
146
|
let el = focusEl;
|
122
147
|
do {
|
123
148
|
el = el.closest(`[popover]:not(:popover-open)`);
|
124
|
-
if ((el === null || el === void 0 ? void 0 : el.popover) === 'auto') {
|
149
|
+
if ((el === null || el === void 0 ? void 0 : el.popover) === 'auto' && !['ArrowRight', 'ArrowLeft'].includes(event.key)) {
|
125
150
|
el.showPopover();
|
126
151
|
}
|
127
152
|
el = (el === null || el === void 0 ? void 0 : el.parentElement) || null;
|
@@ -131,7 +156,7 @@ export default class FocusGroupElement extends HTMLElement {
|
|
131
156
|
}
|
132
157
|
}
|
133
158
|
}
|
134
|
-
_FocusGroupElement_abortController = new WeakMap(), _FocusGroupElement_instances = new WeakSet(), _FocusGroupElement_items_get = function _FocusGroupElement_items_get() {
|
159
|
+
_FocusGroupElement_retainSignal = new WeakMap(), _FocusGroupElement_abortController = new WeakMap(), _FocusGroupElement_instances = new WeakSet(), _FocusGroupElement_items_get = function _FocusGroupElement_items_get() {
|
135
160
|
return this.querySelectorAll(menuItemSelector);
|
136
161
|
};
|
137
162
|
if (!customElements.get('focus-group')) {
|
@@ -8,6 +8,8 @@ const getMnemonicFor = (item: Element) => item.textContent?.trim()[0].toLowerCas
|
|
8
8
|
const printable = /^\S$/
|
9
9
|
|
10
10
|
export default class FocusGroupElement extends HTMLElement {
|
11
|
+
#retainSignal: AbortController | null = null
|
12
|
+
|
11
13
|
get nowrap(): boolean {
|
12
14
|
return this.hasAttribute('nowrap')
|
13
15
|
}
|
@@ -60,8 +62,32 @@ export default class FocusGroupElement extends HTMLElement {
|
|
60
62
|
const {direction, nowrap} = this
|
61
63
|
if (event.type === 'focusin') {
|
62
64
|
if (this.retain && event.target instanceof Element && event.target.matches(menuItemSelector)) {
|
65
|
+
this.#retainSignal?.abort()
|
66
|
+
const {signal} = (this.#retainSignal = new AbortController())
|
63
67
|
for (const item of this.#items) {
|
64
68
|
item.setAttribute('tabindex', item === event.target ? '0' : '-1')
|
69
|
+
const popover = event.target.closest<HTMLElement>('[popover]')
|
70
|
+
if (item === event.target && popover?.popover === 'auto' && popover.closest('focus-group') === this) {
|
71
|
+
popover.addEventListener(
|
72
|
+
'toggle',
|
73
|
+
(toggleEvent: Event) => {
|
74
|
+
if (!(toggleEvent.target instanceof Element)) return
|
75
|
+
if ((toggleEvent as ToggleEvent).newState === 'closed') {
|
76
|
+
this.#retainSignal?.abort()
|
77
|
+
item.setAttribute('tabindex', '-1')
|
78
|
+
if (popover.id) {
|
79
|
+
const invoker = this.querySelector(`[popovertarget="${popover.id}"]`)
|
80
|
+
if (invoker) {
|
81
|
+
invoker.setAttribute('tabindex', '0')
|
82
|
+
} else {
|
83
|
+
this.#items[0]?.setAttribute('tabindex', '0')
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
},
|
88
|
+
{signal}
|
89
|
+
)
|
90
|
+
}
|
65
91
|
}
|
66
92
|
}
|
67
93
|
} else if (event instanceof KeyboardEvent) {
|
@@ -111,7 +137,7 @@ export default class FocusGroupElement extends HTMLElement {
|
|
111
137
|
let el: HTMLElement | null = focusEl
|
112
138
|
do {
|
113
139
|
el = el.closest(`[popover]:not(:popover-open)`)
|
114
|
-
if (el?.popover === 'auto') {
|
140
|
+
if (el?.popover === 'auto' && !['ArrowRight', 'ArrowLeft'].includes(event.key)) {
|
115
141
|
el.showPopover()
|
116
142
|
}
|
117
143
|
el = el?.parentElement || null
|
@@ -8,14 +8,15 @@
|
|
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") %>
|
12
|
-
<% component.with_item(label: "Navigate", tag: :a, content_arguments: { href: action_menu_landing_path }) %>
|
13
|
-
<% component.with_item(label: "Copy text", tag: :"clipboard-copy", content_arguments: { value: "Text to copy" }) %>
|
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
14
|
<% component.with_item(
|
15
15
|
label: "Submit form",
|
16
16
|
href: action_menu_form_action_path,
|
17
17
|
form_arguments: {
|
18
18
|
name: "foo", value: "bar", method: :post
|
19
|
-
}
|
19
|
+
},
|
20
|
+
disabled: disable_items
|
20
21
|
) %>
|
21
22
|
<% end %>
|
@@ -216,7 +216,10 @@ module Primer
|
|
216
216
|
|
217
217
|
# @label With actions
|
218
218
|
#
|
219
|
-
|
219
|
+
# @param disable_items toggle
|
220
|
+
def with_actions(disable_items: false)
|
221
|
+
render_with_template(locals: { disable_items: disable_items })
|
222
|
+
end
|
220
223
|
|
221
224
|
# @label Single select form
|
222
225
|
#
|
data/static/arguments.json
CHANGED
@@ -46,7 +46,18 @@
|
|
46
46
|
"source": "https://github.com/primer/view_components/tree/main/app/components/primer/alpha/action_bar/item.rb",
|
47
47
|
"lookbook": "https://primer.style/view-components/lookbook/inspect/primer/alpha/action_bar/item/default/",
|
48
48
|
"parameters": [
|
49
|
-
|
49
|
+
{
|
50
|
+
"name": "item_content",
|
51
|
+
"type": "String",
|
52
|
+
"default": "N/A",
|
53
|
+
"description": "The content to render inside the item."
|
54
|
+
},
|
55
|
+
{
|
56
|
+
"name": "item_arguments",
|
57
|
+
"type": "Hash",
|
58
|
+
"default": "N/A",
|
59
|
+
"description": "[System arguments](/system-arguments)"
|
60
|
+
}
|
50
61
|
]
|
51
62
|
},
|
52
63
|
{
|
data/static/info_arch.json
CHANGED
@@ -139,7 +139,18 @@
|
|
139
139
|
"source": "https://github.com/primer/view_components/tree/main/app/components/primer/alpha/action_bar/item.rb",
|
140
140
|
"lookbook": "https://primer.style/view-components/lookbook/inspect/primer/alpha/action_bar/item/default/",
|
141
141
|
"parameters": [
|
142
|
-
|
142
|
+
{
|
143
|
+
"name": "item_content",
|
144
|
+
"type": "String",
|
145
|
+
"default": "N/A",
|
146
|
+
"description": "The content to render inside the item."
|
147
|
+
},
|
148
|
+
{
|
149
|
+
"name": "item_arguments",
|
150
|
+
"type": "Hash",
|
151
|
+
"default": "N/A",
|
152
|
+
"description": "{{link_to_system_arguments_docs}}"
|
153
|
+
}
|
143
154
|
],
|
144
155
|
"slots": [
|
145
156
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: primer_view_components
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09
|
11
|
+
date: 2023-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionview
|