primer_view_components 0.9.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +78 -0
- data/app/assets/javascripts/app/components/primer/primer.d.ts +1 -1
- 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_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 +170 -85
- data/app/components/primer/alpha/action_menu/action_menu_element.ts +206 -80
- data/app/components/primer/alpha/action_menu/list.rb +0 -2
- data/app/components/primer/alpha/check_box_group.rb +2 -0
- data/app/components/primer/alpha/dialog/header.rb +12 -0
- data/app/components/primer/alpha/dialog.rb +1 -1
- data/app/components/primer/alpha/nav_list/divider.rb +2 -5
- data/app/components/primer/alpha/nav_list/group.rb +2 -98
- data/app/components/primer/alpha/nav_list/heading.rb +2 -27
- data/app/components/primer/alpha/nav_list/item.rb +2 -147
- data/app/components/primer/alpha/nav_list.rb +2 -205
- data/app/components/primer/alpha/overlay.css +1 -1
- data/app/components/primer/alpha/overlay.css.map +1 -1
- data/app/components/primer/alpha/overlay.pcss +1 -7
- data/app/components/primer/alpha/overlay.rb +6 -4
- data/app/components/primer/alpha/radio_button_group.rb +2 -0
- data/app/components/primer/alpha/text_field.css +1 -1
- data/app/components/primer/alpha/text_field.css.json +4 -1
- data/app/components/primer/alpha/text_field.css.map +1 -1
- data/app/components/primer/alpha/text_field.pcss +18 -3
- data/app/components/primer/alpha/tooltip.rb +3 -1
- data/app/components/primer/beta/button.css +1 -1
- data/app/components/primer/beta/button.css.json +2 -0
- data/app/components/primer/beta/button.css.map +1 -1
- data/app/components/primer/beta/button.pcss +11 -3
- data/app/components/primer/beta/icon_button.html.erb +1 -1
- data/app/components/primer/beta/icon_button.rb +8 -1
- data/app/components/primer/beta/link.css +1 -1
- data/app/components/primer/beta/link.css.json +1 -0
- data/app/components/primer/beta/link.css.map +1 -1
- data/app/components/primer/beta/link.pcss +5 -0
- data/app/components/primer/beta/link.rb +2 -2
- data/app/components/primer/beta/nav_list/divider.rb +14 -0
- data/app/components/primer/beta/nav_list/group.rb +107 -0
- data/app/components/primer/beta/nav_list/heading.rb +36 -0
- data/app/components/primer/beta/nav_list/item.rb +156 -0
- data/app/components/primer/beta/nav_list.rb +212 -0
- data/app/components/primer/focus_group.js +30 -4
- data/app/components/primer/focus_group.ts +29 -2
- data/app/components/primer/primer.d.ts +1 -1
- data/app/components/primer/primer.js +1 -1
- data/app/components/primer/primer.ts +1 -1
- data/app/helpers/primer/form_helper.rb +10 -0
- data/lib/primer/deprecations.yml +20 -0
- data/lib/primer/forms/check_box_group.html.erb +3 -0
- data/lib/primer/forms/dsl/check_box_group_input.rb +1 -5
- data/lib/primer/forms/dsl/check_box_input.rb +5 -0
- data/lib/primer/forms/dsl/radio_button_input.rb +5 -0
- data/lib/primer/forms/form_control.html.erb +1 -4
- data/lib/primer/forms/radio_button_group.html.erb +3 -0
- data/lib/primer/forms/utils.rb +2 -0
- data/lib/primer/forms/validation_message.html.erb +4 -0
- data/lib/primer/forms/validation_message.rb +14 -0
- data/lib/primer/forms.rb +16 -0
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/primer/yard/component_manifest.rb +4 -0
- data/previews/primer/alpha/action_menu_preview/with_actions.html.erb +5 -4
- data/previews/primer/alpha/action_menu_preview.rb +4 -1
- data/previews/primer/alpha/check_box_group_preview.rb +13 -0
- data/previews/primer/alpha/dialog_preview/with_header.html.erb +5 -0
- data/previews/primer/alpha/dialog_preview.rb +17 -0
- data/previews/primer/alpha/overlay_preview.rb +1 -1
- data/previews/primer/alpha/radio_button_group_preview.rb +13 -0
- data/previews/primer/alpha/radio_button_preview.rb +1 -1
- data/previews/primer/alpha/text_field_preview/input_group_leading_action_menu.html.erb +21 -0
- data/previews/primer/alpha/text_field_preview/input_group_leading_button.html.erb +18 -0
- data/previews/primer/alpha/text_field_preview/input_group_trailing_button.html.erb +18 -0
- data/previews/primer/alpha/text_field_preview.rb +21 -0
- data/previews/primer/beta/button_preview.rb +1 -1
- data/previews/primer/{alpha → beta}/nav_list_preview/trailing_action.html.erb +1 -1
- data/previews/primer/{alpha → beta}/nav_list_preview.rb +5 -5
- data/static/arguments.json +194 -7
- data/static/audited_at.json +5 -0
- data/static/classes.json +3 -0
- data/static/constants.json +23 -0
- data/static/info_arch.json +1015 -527
- data/static/previews.json +85 -7
- data/static/statuses.json +10 -5
- metadata +23 -11
- /data/app/assets/javascripts/app/components/primer/{alpha → beta}/nav_list.d.ts +0 -0
- /data/app/components/primer/{alpha → beta}/nav_list/group.html.erb +0 -0
- /data/app/components/primer/{alpha → beta}/nav_list/item.html.erb +0 -0
- /data/app/components/primer/{alpha → beta}/nav_list.d.ts +0 -0
- /data/app/components/primer/{alpha → beta}/nav_list.html.erb +0 -0
- /data/app/components/primer/{alpha → beta}/nav_list.js +0 -0
- /data/app/components/primer/{alpha → beta}/nav_list.ts +0 -0
@@ -9,7 +9,8 @@ type SelectedItem = {
|
|
9
9
|
element: Element
|
10
10
|
}
|
11
11
|
|
12
|
-
const
|
12
|
+
const validSelectors = ['[role="menuitem"]', '[role="menuitemcheckbox"]', '[role="menuitemradio"]']
|
13
|
+
const menuItemSelectors = validSelectors.map(selector => `:not([hidden]) > ${selector}`)
|
13
14
|
|
14
15
|
@controller
|
15
16
|
export class ActionMenuElement extends HTMLElement {
|
@@ -19,6 +20,7 @@ export class ActionMenuElement extends HTMLElement {
|
|
19
20
|
#abortController: AbortController
|
20
21
|
#originalLabel = ''
|
21
22
|
#inputName = ''
|
23
|
+
#invokerBeingClicked = false
|
22
24
|
|
23
25
|
get selectVariant(): SelectVariant {
|
24
26
|
return this.getAttribute('data-select-variant') as SelectVariant
|
@@ -51,7 +53,7 @@ export class ActionMenuElement extends HTMLElement {
|
|
51
53
|
}
|
52
54
|
|
53
55
|
get popoverElement(): HTMLElement | null {
|
54
|
-
return this.invokerElement?.popoverTargetElement || null
|
56
|
+
return (this.invokerElement?.popoverTargetElement as HTMLElement) || null
|
55
57
|
}
|
56
58
|
|
57
59
|
get invokerElement(): HTMLButtonElement | null {
|
@@ -93,8 +95,10 @@ export class ActionMenuElement extends HTMLElement {
|
|
93
95
|
this.addEventListener('click', this, {signal})
|
94
96
|
this.addEventListener('mouseover', this, {signal})
|
95
97
|
this.addEventListener('focusout', this, {signal})
|
98
|
+
this.addEventListener('mousedown', this, {signal})
|
96
99
|
this.#setDynamicLabel()
|
97
100
|
this.#updateInput()
|
101
|
+
this.#softDisableItems()
|
98
102
|
|
99
103
|
if (this.includeFragment) {
|
100
104
|
this.includeFragment.addEventListener('include-fragment-replaced', this, {
|
@@ -103,19 +107,69 @@ export class ActionMenuElement extends HTMLElement {
|
|
103
107
|
}
|
104
108
|
}
|
105
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
|
+
|
106
132
|
disconnectedCallback() {
|
107
133
|
this.#abortController.abort()
|
108
134
|
}
|
109
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
|
+
|
110
153
|
handleEvent(event: Event) {
|
111
|
-
const
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
119
173
|
}
|
120
174
|
|
121
175
|
// Ignore events within dialogs within menus
|
@@ -123,84 +177,161 @@ export class ActionMenuElement extends HTMLElement {
|
|
123
177
|
return
|
124
178
|
}
|
125
179
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
this.querySelector<HTMLElement>('.ActionListWrap')!.style.display = 'none'
|
134
|
-
const dialog_controller = new AbortController()
|
135
|
-
const {signal} = dialog_controller
|
136
|
-
const handleDialogClose = () => {
|
137
|
-
dialog_controller.abort()
|
138
|
-
this.querySelector<HTMLElement>('.ActionListWrap')!.style.display = ''
|
139
|
-
if (this.popoverElement?.matches(':popover-open')) {
|
140
|
-
this.popoverElement?.hidePopover()
|
141
|
-
}
|
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()
|
142
187
|
}
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
}
|
188
|
+
})
|
189
|
+
|
190
|
+
return
|
147
191
|
}
|
148
192
|
|
149
|
-
|
193
|
+
const item = (event.target as Element).closest(menuItemSelectors.join(','))
|
194
|
+
const targetIsItem = item !== null
|
150
195
|
|
151
|
-
if (
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
setTimeout(() => {
|
162
|
-
if (this.popoverElement?.matches(':popover-open')) {
|
163
|
-
this.popoverElement?.hidePopover()
|
164
|
-
}
|
165
|
-
})
|
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
|
+
}
|
166
206
|
}
|
167
207
|
|
168
|
-
|
169
|
-
|
170
|
-
|
208
|
+
this.#activateItem(event, item)
|
209
|
+
this.#handleItemActivated(event, item)
|
210
|
+
return
|
211
|
+
}
|
171
212
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
213
|
+
if (event.type === 'include-fragment-replaced') {
|
214
|
+
this.#handleIncludeFragmentReplaced()
|
215
|
+
}
|
216
|
+
}
|
176
217
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
item.setAttribute('aria-checked', 'true')
|
181
|
-
}
|
218
|
+
#handleInvokerActivated(event: Event) {
|
219
|
+
event.preventDefault()
|
220
|
+
event.stopPropagation()
|
182
221
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
222
|
+
if (this.#isOpen()) {
|
223
|
+
this.#hide()
|
224
|
+
} else {
|
225
|
+
this.#show()
|
226
|
+
this.#firstItem?.focus()
|
227
|
+
}
|
228
|
+
}
|
188
229
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
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()
|
193
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
|
194
263
|
|
195
|
-
|
264
|
+
const ariaChecked = item.getAttribute('aria-checked')
|
265
|
+
const checked = ariaChecked !== 'true'
|
196
266
|
|
197
|
-
|
198
|
-
|
199
|
-
|
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
|
+
}
|
200
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
|
201
291
|
}
|
202
292
|
}
|
203
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
|
+
|
204
335
|
#setDynamicLabel() {
|
205
336
|
if (!this.dynamicLabel) return
|
206
337
|
const invokerLabel = this.invokerLabel
|
@@ -260,18 +391,13 @@ export class ActionMenuElement extends HTMLElement {
|
|
260
391
|
}
|
261
392
|
}
|
262
393
|
|
263
|
-
#isActivationKeydown(event: Event): boolean {
|
264
|
-
return (
|
265
|
-
event instanceof KeyboardEvent &&
|
266
|
-
event.type === 'keydown' &&
|
267
|
-
!(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
|
268
|
-
(event.key === 'Enter' || event.key === ' ')
|
269
|
-
)
|
270
|
-
}
|
271
|
-
|
272
394
|
get #firstItem(): HTMLElement | null {
|
273
395
|
return this.querySelector(menuItemSelectors.join(','))
|
274
396
|
}
|
397
|
+
|
398
|
+
get #items(): HTMLElement[] {
|
399
|
+
return Array.from(this.querySelectorAll(menuItemSelectors.join(',')))
|
400
|
+
}
|
275
401
|
}
|
276
402
|
|
277
403
|
if (!window.customElements.get('action-menu')) {
|
@@ -23,6 +23,8 @@ module Primer
|
|
23
23
|
# @param label [String] Label text displayed above the input.
|
24
24
|
# @param hidden [Boolean] When set to `true`, visually hides the group.
|
25
25
|
# @param caption [String] A string describing the field and what sorts of input it expects. Displayed below the group.
|
26
|
+
# @param invalid [Boolean] If set to `true`, the input will be marked as invalid. Implied if `validation_message` is truthy. This option is set to `true` automatically if the model object associated with the form reports that the input is invalid via Rails validations. It is provided for cases where the form does not have an associated model. If the input is invalid as determined by Rails validations, setting `invalid` to `false` will have no effect.
|
27
|
+
# @param validation_message [String] A string displayed between the caption and the input indicating the input's contents are invalid. This option is, by default, set to the first Rails validation message for the input (assuming the form is associated with a model object). Use `validation_message` to override the default or to provide a validation message in case there is no associated model object.
|
26
28
|
# @param label_arguments [Hash] Attributes that will be passed to Rails' `builder.label` method. These can be HTML attributes or any of the other label options Rails supports. They will appear as HTML attributes on the `<label>` tag.
|
27
29
|
|
28
30
|
# @!method check_box
|
@@ -9,10 +9,19 @@ module Primer
|
|
9
9
|
status :alpha
|
10
10
|
audited_at "2022-10-10"
|
11
11
|
|
12
|
+
DEFAULT_VARIANT = :medium
|
13
|
+
VARIANT_MAPPINGS = {
|
14
|
+
DEFAULT_VARIANT => "",
|
15
|
+
:large => "Overlay-header--large"
|
16
|
+
}.freeze
|
17
|
+
VARIANT_OPTIONS = VARIANT_MAPPINGS.keys
|
18
|
+
|
19
|
+
# @param id [String] The HTML element's ID value.
|
12
20
|
# @param title [String] Describes the content of the dialog.
|
13
21
|
# @param subtitle [String] Provides dditional context for the dialog, also setting the `aria-describedby` attribute.
|
14
22
|
# @param show_divider [Boolean] Show a divider between the header and body.
|
15
23
|
# @param visually_hide_title [Boolean] Visually hide the `title` while maintaining a label for assistive technologies.
|
24
|
+
# @param variant [Symbol] <%= one_of(Primer::Alpha::Dialog::Header::VARIANT_OPTIONS) %>
|
16
25
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
17
26
|
def initialize(
|
18
27
|
id:,
|
@@ -20,6 +29,7 @@ module Primer
|
|
20
29
|
subtitle: nil,
|
21
30
|
show_divider: false,
|
22
31
|
visually_hide_title: false,
|
32
|
+
variant: DEFAULT_VARIANT,
|
23
33
|
**system_arguments
|
24
34
|
)
|
25
35
|
@id = id
|
@@ -28,8 +38,10 @@ module Primer
|
|
28
38
|
@visually_hide_title = visually_hide_title
|
29
39
|
@system_arguments = deny_tag_argument(**system_arguments)
|
30
40
|
@system_arguments[:tag] = :div
|
41
|
+
|
31
42
|
@system_arguments[:classes] = class_names(
|
32
43
|
"Overlay-header",
|
44
|
+
VARIANT_MAPPINGS[fetch_or_fallback(VARIANT_OPTIONS, variant, DEFAULT_VARIANT)],
|
33
45
|
{ "Overlay-header--divided": show_divider },
|
34
46
|
system_arguments[:classes]
|
35
47
|
)
|
@@ -74,7 +74,7 @@ module Primer
|
|
74
74
|
#
|
75
75
|
# @param show_divider [Boolean] Show a divider between the header and body.
|
76
76
|
# @param visually_hide_title [Boolean] Visually hide the `title` while maintaining a label for assistive technologies.
|
77
|
-
# @param system_arguments [Hash] <%=
|
77
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::Dialog::Header) %>.
|
78
78
|
renders_one :header, lambda { |show_divider: false, visually_hide_title: @visually_hide_title, **system_arguments|
|
79
79
|
Primer::Alpha::Dialog::Header.new(
|
80
80
|
id: @id,
|
@@ -3,11 +3,8 @@
|
|
3
3
|
module Primer
|
4
4
|
module Alpha
|
5
5
|
class NavList
|
6
|
-
|
7
|
-
|
8
|
-
def kind
|
9
|
-
:divider
|
10
|
-
end
|
6
|
+
class Divider < Beta::NavList::Divider
|
7
|
+
status :deprecated
|
11
8
|
end
|
12
9
|
end
|
13
10
|
end
|
@@ -3,104 +3,8 @@
|
|
3
3
|
module Primer
|
4
4
|
module Alpha
|
5
5
|
class NavList
|
6
|
-
|
7
|
-
|
8
|
-
# See <%= link_to_component(Primer::Alpha::NavList) %> for usage examples.
|
9
|
-
class Group < ActionList
|
10
|
-
# A special "show more" list item that appears at the bottom of the group. Clicking
|
11
|
-
# the item will fetch the next page of results from the URL passed in the `src` argument
|
12
|
-
# and append the resulting chunk of HTML to the group.
|
13
|
-
#
|
14
|
-
# @param src [String] The URL to query for additional pages of list items.
|
15
|
-
# @param pages [Integer] The total number of pages in the result set.
|
16
|
-
# @param component_klass [Class] A component class to use instead of the default `Primer::Alpha::NavList::Item` class.
|
17
|
-
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::NavList::Item) %>.
|
18
|
-
renders_one :show_more_item, lambda { |src:, pages:, component_klass: NavList::Item, **system_arguments|
|
19
|
-
system_arguments[:classes] = class_names(
|
20
|
-
@item_classes,
|
21
|
-
system_arguments[:classes]
|
22
|
-
)
|
23
|
-
system_arguments[:tag] = :div
|
24
|
-
system_arguments[:id] ||= self.class.generate_id(base_name: "item")
|
25
|
-
system_arguments[:hidden] = true
|
26
|
-
system_arguments[:href] = "#"
|
27
|
-
system_arguments[:data] ||= {}
|
28
|
-
system_arguments[:data][:target] = "nav-list.showMoreItem"
|
29
|
-
system_arguments[:data][:action] = "click:nav-list#showMore"
|
30
|
-
system_arguments[:data][:current_page] = "1"
|
31
|
-
system_arguments[:data][:total_pages] = pages.to_s
|
32
|
-
system_arguments[:label_arguments] = {
|
33
|
-
**system_arguments[:label_arguments] || {},
|
34
|
-
color: :accent
|
35
|
-
}
|
36
|
-
|
37
|
-
system_arguments[:content_arguments] = {
|
38
|
-
**system_arguments[:content_arguments] || {},
|
39
|
-
tag: :button
|
40
|
-
}
|
41
|
-
|
42
|
-
system_arguments[:content_arguments][:data] = merge_data(
|
43
|
-
system_arguments[:content_arguments],
|
44
|
-
data: { list_id: id }
|
45
|
-
)
|
46
|
-
|
47
|
-
component_klass.new(list: self, src: src, **system_arguments)
|
48
|
-
}
|
49
|
-
|
50
|
-
# @private
|
51
|
-
def self.custom_element_name
|
52
|
-
Primer::Alpha::NavList.custom_element_name
|
53
|
-
end
|
54
|
-
|
55
|
-
# @param selected_item_id [Symbol] The ID of the currently selected item. Used internally.
|
56
|
-
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
57
|
-
def initialize(selected_item_id: nil, **system_arguments)
|
58
|
-
@system_arguments = system_arguments
|
59
|
-
@selected_item_id = selected_item_id
|
60
|
-
|
61
|
-
super(**@system_arguments)
|
62
|
-
end
|
63
|
-
|
64
|
-
# Cause this group to show its list of sub items when rendered.
|
65
|
-
# :nocov:
|
66
|
-
def expand!
|
67
|
-
@expanded = true
|
68
|
-
end
|
69
|
-
# :nocov:
|
70
|
-
|
71
|
-
# @!parse
|
72
|
-
# # Items.
|
73
|
-
# #
|
74
|
-
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::NavList::Item) %>.
|
75
|
-
# renders_many :items
|
76
|
-
|
77
|
-
# @private
|
78
|
-
def build_item(component_klass: NavList::Item, **system_arguments)
|
79
|
-
super(
|
80
|
-
component_klass: component_klass,
|
81
|
-
selected_item_id: @selected_item_id,
|
82
|
-
**system_arguments
|
83
|
-
)
|
84
|
-
end
|
85
|
-
|
86
|
-
# @private
|
87
|
-
def build_avatar_item(component_klass: NavList::Item, **system_arguments)
|
88
|
-
super(
|
89
|
-
component_klass: component_klass,
|
90
|
-
selected_item_id: @selected_item_id,
|
91
|
-
**system_arguments
|
92
|
-
)
|
93
|
-
end
|
94
|
-
|
95
|
-
def kind
|
96
|
-
:group
|
97
|
-
end
|
98
|
-
|
99
|
-
def before_render
|
100
|
-
super
|
101
|
-
|
102
|
-
raise ArgumentError, "NavList groups are required to have headings" unless heading?
|
103
|
-
end
|
6
|
+
class Group < Beta::NavList::Group
|
7
|
+
status :deprecated
|
104
8
|
end
|
105
9
|
end
|
106
10
|
end
|
@@ -3,33 +3,8 @@
|
|
3
3
|
module Primer
|
4
4
|
module Alpha
|
5
5
|
class NavList
|
6
|
-
|
7
|
-
|
8
|
-
# See <%= link_to_component(Primer::Alpha::NavList) %> for usage examples.
|
9
|
-
class Heading < Primer::Component
|
10
|
-
attr_reader :title, :id, :heading_level, :system_arguments
|
11
|
-
|
12
|
-
# @param title [String] The text content of the heading.
|
13
|
-
# @param id [String] The value of the ID HTML attribute. Auto-generated by default.
|
14
|
-
# @param heading_level [Integer] The heading level, i.e. 2 for an `<h2>`, 3 for an `<h3>`, etc.
|
15
|
-
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
16
|
-
def initialize(title:, id: self.class.generate_id, heading_level: 2, **system_arguments)
|
17
|
-
@title = title
|
18
|
-
@id = id
|
19
|
-
@heading_level = heading_level
|
20
|
-
@system_arguments = system_arguments
|
21
|
-
end
|
22
|
-
|
23
|
-
def call
|
24
|
-
render(
|
25
|
-
Primer::BaseComponent.new(
|
26
|
-
tag: :"h#{heading_level}",
|
27
|
-
id: id,
|
28
|
-
classes: "ActionListHeader",
|
29
|
-
**system_arguments
|
30
|
-
).with_content(title)
|
31
|
-
)
|
32
|
-
end
|
6
|
+
class Heading < Beta::NavList::Heading
|
7
|
+
status :deprecated
|
33
8
|
end
|
34
9
|
end
|
35
10
|
end
|