openproject-primer_view_components 0.34.0 → 0.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/app/assets/javascripts/lib/primer/forms/primer_text_field.d.ts +27 -0
  4. data/app/assets/javascripts/primer_view_components.js +1 -1
  5. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  6. data/app/assets/styles/primer_view_components.css +1 -1
  7. data/app/assets/styles/primer_view_components.css.map +1 -1
  8. data/app/components/primer/alpha/action_menu/action_menu_element.js +13 -43
  9. data/app/components/primer/alpha/action_menu/action_menu_element.ts +13 -51
  10. data/app/components/primer/alpha/tab_nav.css +1 -1
  11. data/app/components/primer/alpha/tab_nav.css.json +2 -0
  12. data/app/components/primer/alpha/tab_nav.css.map +1 -1
  13. data/app/components/primer/alpha/tab_nav.pcss +11 -1
  14. data/app/components/primer/alpha/tab_panels.html.erb +5 -9
  15. data/app/components/primer/alpha/tab_panels.rb +4 -13
  16. data/app/components/primer/alpha/text_field.rb +1 -0
  17. data/app/components/primer/alpha/underline_nav.css +1 -1
  18. data/app/components/primer/alpha/underline_nav.css.json +2 -0
  19. data/app/components/primer/alpha/underline_nav.css.map +1 -1
  20. data/app/components/primer/alpha/underline_nav.pcss +7 -1
  21. data/app/components/primer/alpha/underline_panels.css +1 -0
  22. data/app/components/primer/alpha/underline_panels.css.json +6 -0
  23. data/app/components/primer/alpha/underline_panels.css.map +1 -0
  24. data/app/components/primer/alpha/underline_panels.html.erb +6 -8
  25. data/app/components/primer/alpha/underline_panels.pcss +4 -0
  26. data/app/components/primer/alpha/underline_panels.rb +6 -14
  27. data/app/components/primer/beta/icon_button.rb +5 -0
  28. data/app/components/primer/beta/relative_time.rb +3 -0
  29. data/app/components/primer/beta/spinner.html.erb +3 -0
  30. data/app/components/primer/beta/spinner.rb +15 -1
  31. data/app/components/primer/open_project/flex_layout.rb +1 -1
  32. data/app/components/primer/primer.pcss +1 -0
  33. data/lib/primer/forms/dsl/text_field_input.rb +8 -1
  34. data/lib/primer/forms/primer_text_field.d.ts +27 -0
  35. data/lib/primer/forms/primer_text_field.js +17 -5
  36. data/lib/primer/forms/primer_text_field.ts +24 -5
  37. data/lib/primer/forms/text_field.html.erb +6 -2
  38. data/lib/primer/view_components/version.rb +2 -2
  39. data/previews/primer/alpha/text_field_preview.rb +11 -0
  40. data/previews/primer/beta/breadcrumbs_preview.rb +9 -0
  41. data/previews/primer/beta/icon_button_preview/summary_as_button.html.erb +12 -0
  42. data/previews/primer/beta/icon_button_preview.rb +95 -14
  43. data/previews/primer/beta/relative_time_preview/link_with_tooltip.html.erb +13 -0
  44. data/previews/primer/beta/relative_time_preview.rb +12 -0
  45. data/previews/primer/beta/spinner_preview.rb +2 -2
  46. data/static/arguments.json +12 -12
  47. data/static/constants.json +1 -0
  48. data/static/info_arch.json +78 -13
  49. data/static/previews.json +65 -0
  50. metadata +9 -3
@@ -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;
18
+ var _ActionMenuElement_instances, _ActionMenuElement_abortController, _ActionMenuElement_originalLabel, _ActionMenuElement_inputName, _ActionMenuElement_invokerBeingClicked, _ActionMenuElement_softDisableItems, _ActionMenuElement_potentiallyDisallowActivation, _ActionMenuElement_isAnchorActivationViaSpace, _ActionMenuElement_isActivation, _ActionMenuElement_handleInvokerActivated, _ActionMenuElement_handleDialogItemActivated, _ActionMenuElement_handleItemActivated, _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"]'];
@@ -152,18 +152,13 @@ let ActionMenuElement = class ActionMenuElement extends HTMLElement {
152
152
  return;
153
153
  }
154
154
  }
155
- __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_activateItem).call(this, event, item);
156
- __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_handleItemActivated).call(this, item);
157
- // Pressing the space key on a button or link will cause the page to scroll unless preventDefault()
158
- // is called. While calling preventDefault() appears to have no effect on link navigation, it skips
159
- // form submission. The code below therefore only calls preventDefault() if the button has been
160
- // activated by the space key, and manually submits the form if the button is a submit button.
161
- if (__classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isKeyboardActivationViaSpace).call(this, event)) {
155
+ // Pressing the space key on a link will cause the page to scroll unless preventDefault() is called.
156
+ // We then click it manually to navigate.
157
+ if (__classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isAnchorActivationViaSpace).call(this, event)) {
162
158
  event.preventDefault();
163
- if (item.getAttribute('type') === 'submit') {
164
- item.closest('form')?.submit();
165
- }
159
+ item.click();
166
160
  }
161
+ __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_handleItemActivated).call(this, item);
167
162
  return;
168
163
  }
169
164
  if (event.type === 'include-fragment-replaced') {
@@ -269,26 +264,18 @@ _ActionMenuElement_potentiallyDisallowActivation = function _ActionMenuElement_p
269
264
  }
270
265
  return false;
271
266
  };
272
- _ActionMenuElement_isKeyboardActivation = function _ActionMenuElement_isKeyboardActivation(event) {
273
- return __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isKeyboardActivationViaEnter).call(this, event) || __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isKeyboardActivationViaSpace).call(this, event);
274
- };
275
- _ActionMenuElement_isKeyboardActivationViaEnter = function _ActionMenuElement_isKeyboardActivationViaEnter(event) {
276
- return (event instanceof KeyboardEvent &&
277
- event.type === 'keydown' &&
278
- !(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
279
- event.key === 'Enter');
280
- };
281
- _ActionMenuElement_isKeyboardActivationViaSpace = function _ActionMenuElement_isKeyboardActivationViaSpace(event) {
282
- return (event instanceof KeyboardEvent &&
267
+ _ActionMenuElement_isAnchorActivationViaSpace = function _ActionMenuElement_isAnchorActivationViaSpace(event) {
268
+ return (event.target instanceof HTMLAnchorElement &&
269
+ event instanceof KeyboardEvent &&
283
270
  event.type === 'keydown' &&
284
271
  !(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
285
272
  event.key === ' ');
286
273
  };
287
- _ActionMenuElement_isMouseActivation = function _ActionMenuElement_isMouseActivation(event) {
288
- return event instanceof MouseEvent && event.type === 'click';
289
- };
290
274
  _ActionMenuElement_isActivation = function _ActionMenuElement_isActivation(event) {
291
- return __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isMouseActivation).call(this, event) || __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isKeyboardActivation).call(this, event);
275
+ // Some browsers fire MouseEvents (Firefox) and others fire PointerEvents (Chrome). Activating an item via
276
+ // enter or space counterintuitively fires one of these rather than a KeyboardEvent. Since PointerEvent
277
+ // inherits from MouseEvent, it is enough to check for MouseEvent here.
278
+ return (event instanceof MouseEvent && event.type === 'click') || __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_isAnchorActivationViaSpace).call(this, event);
292
279
  };
293
280
  _ActionMenuElement_handleInvokerActivated = function _ActionMenuElement_handleInvokerActivated(event) {
294
281
  event.preventDefault();
@@ -363,23 +350,6 @@ _ActionMenuElement_handleItemActivated = function _ActionMenuElement_handleItemA
363
350
  detail: { item: item.parentElement, checked: this.isItemChecked(item.parentElement) },
364
351
  }));
365
352
  };
366
- _ActionMenuElement_activateItem = function _ActionMenuElement_activateItem(event, item) {
367
- const eventWillActivateByDefault = (event instanceof MouseEvent && event.type === 'click') ||
368
- (event instanceof KeyboardEvent &&
369
- event.type === 'keydown' &&
370
- !(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
371
- event.key === 'Enter');
372
- // if the event will result in activating the current item by default, i.e. is a
373
- // mouse click or keyboard enter, bail out
374
- if (eventWillActivateByDefault)
375
- return;
376
- // otherwise, event will not result in activation by default, so we stop it and
377
- // simulate a click
378
- /* eslint-disable-next-line no-restricted-syntax */
379
- event.stopPropagation();
380
- const elem = item;
381
- elem.click();
382
- };
383
353
  _ActionMenuElement_handleIncludeFragmentReplaced = function _ActionMenuElement_handleIncludeFragmentReplaced() {
384
354
  if (__classPrivateFieldGet(this, _ActionMenuElement_instances, "a", _ActionMenuElement_firstItem_get))
385
355
  __classPrivateFieldGet(this, _ActionMenuElement_instances, "a", _ActionMenuElement_firstItem_get).focus();
@@ -151,21 +151,9 @@ export class ActionMenuElement extends HTMLElement {
151
151
  return false
152
152
  }
153
153
 
154
- #isKeyboardActivation(event: Event): boolean {
155
- return this.#isKeyboardActivationViaEnter(event) || this.#isKeyboardActivationViaSpace(event)
156
- }
157
-
158
- #isKeyboardActivationViaEnter(event: Event): boolean {
159
- return (
160
- event instanceof KeyboardEvent &&
161
- event.type === 'keydown' &&
162
- !(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
163
- event.key === 'Enter'
164
- )
165
- }
166
-
167
- #isKeyboardActivationViaSpace(event: Event): boolean {
154
+ #isAnchorActivationViaSpace(event: Event): boolean {
168
155
  return (
156
+ event.target instanceof HTMLAnchorElement &&
169
157
  event instanceof KeyboardEvent &&
170
158
  event.type === 'keydown' &&
171
159
  !(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
@@ -173,12 +161,11 @@ export class ActionMenuElement extends HTMLElement {
173
161
  )
174
162
  }
175
163
 
176
- #isMouseActivation(event: Event): boolean {
177
- return event instanceof MouseEvent && event.type === 'click'
178
- }
179
-
180
164
  #isActivation(event: Event): boolean {
181
- return this.#isMouseActivation(event) || this.#isKeyboardActivation(event)
165
+ // Some browsers fire MouseEvents (Firefox) and others fire PointerEvents (Chrome). Activating an item via
166
+ // enter or space counterintuitively fires one of these rather than a KeyboardEvent. Since PointerEvent
167
+ // inherits from MouseEvent, it is enough to check for MouseEvent here.
168
+ return (event instanceof MouseEvent && event.type === 'click') || this.#isAnchorActivationViaSpace(event)
182
169
  }
183
170
 
184
171
  handleEvent(event: Event) {
@@ -237,21 +224,15 @@ export class ActionMenuElement extends HTMLElement {
237
224
  }
238
225
  }
239
226
 
240
- this.#activateItem(event, item)
241
- this.#handleItemActivated(item)
242
-
243
- // Pressing the space key on a button or link will cause the page to scroll unless preventDefault()
244
- // is called. While calling preventDefault() appears to have no effect on link navigation, it skips
245
- // form submission. The code below therefore only calls preventDefault() if the button has been
246
- // activated by the space key, and manually submits the form if the button is a submit button.
247
- if (this.#isKeyboardActivationViaSpace(event)) {
227
+ // Pressing the space key on a link will cause the page to scroll unless preventDefault() is called.
228
+ // We then click it manually to navigate.
229
+ if (this.#isAnchorActivationViaSpace(event)) {
248
230
  event.preventDefault()
249
-
250
- if (item.getAttribute('type') === 'submit') {
251
- item.closest('form')?.submit()
252
- }
231
+ ;(item as HTMLElement).click()
253
232
  }
254
233
 
234
+ this.#handleItemActivated(item)
235
+
255
236
  return
256
237
  }
257
238
 
@@ -335,6 +316,7 @@ export class ActionMenuElement extends HTMLElement {
335
316
  }
336
317
 
337
318
  this.#updateInput()
319
+
338
320
  this.dispatchEvent(
339
321
  new CustomEvent('itemActivated', {
340
322
  detail: {item: item.parentElement, checked: this.isItemChecked(item.parentElement)},
@@ -342,26 +324,6 @@ export class ActionMenuElement extends HTMLElement {
342
324
  )
343
325
  }
344
326
 
345
- #activateItem(event: Event, item: Element) {
346
- const eventWillActivateByDefault =
347
- (event instanceof MouseEvent && event.type === 'click') ||
348
- (event instanceof KeyboardEvent &&
349
- event.type === 'keydown' &&
350
- !(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
351
- event.key === 'Enter')
352
-
353
- // if the event will result in activating the current item by default, i.e. is a
354
- // mouse click or keyboard enter, bail out
355
- if (eventWillActivateByDefault) return
356
-
357
- // otherwise, event will not result in activation by default, so we stop it and
358
- // simulate a click
359
- /* eslint-disable-next-line no-restricted-syntax */
360
- event.stopPropagation()
361
- const elem = item as HTMLElement
362
- elem.click()
363
- }
364
-
365
327
  #handleIncludeFragmentReplaced() {
366
328
  if (this.#firstItem) this.#firstItem.focus()
367
329
  this.#softDisableItems()
@@ -1 +1 @@
1
- .tabnav{border-bottom:var(--borderWidth-thin) solid var(--borderColor-default);margin-bottom:var(--stack-gap-normal);margin-top:0}.tabnav-tabs{display:flex;margin-bottom:calc(var(--borderWidth-thin)*-1);overflow:auto}.tabnav-tab{background-color:initial;border:var(--borderWidth-thin) solid #0000;border-bottom:0;color:var(--fgColor-muted);display:inline-block;flex-shrink:0;font-size:var(--text-body-size-medium);line-height:23px;padding:var(--base-size-8) var(--control-medium-paddingInline-spacious);-webkit-text-decoration:none;text-decoration:none;transition:color .2s cubic-bezier(.3,0,.5,1)}.tabnav-tab.selected,.tabnav-tab[aria-current]:not([aria-current=false]),.tabnav-tab[aria-selected=true]{background-color:var(--bgColor-default);border-color:var(--borderColor-default);border-radius:var(--borderRadius-medium) var(--borderRadius-medium) 0 0;color:var(--fgColor-default)}.tabnav-tab.selected .octicon,.tabnav-tab[aria-current]:not([aria-current=false]) .octicon,.tabnav-tab[aria-selected=true] .octicon{color:inherit}.tabnav-tab:hover{color:var(--fgColor-default);-webkit-text-decoration:none;text-decoration:none;transition-duration:.1s}.tabnav-tab:focus,.tabnav-tab:focus-visible{border-radius:var(--borderRadius-medium) var(--borderRadius-medium) 0 0!important;outline-offset:-6px}.tabnav-tab .octicon,.tabnav-tab:active{color:var(--fgColor-muted)}.tabnav-tab .octicon{margin-right:var(--control-small-gap)}.tabnav-tab .Counter{color:inherit;margin-left:var(--control-small-gap)}.tabnav-extra{color:var(--fgColor-muted);display:inline-block;font-size:var(--text-body-size-small);margin-left:10px;padding-top:10px}.tabnav-extra>.octicon{margin-right:2px}a.tabnav-extra:hover{color:var(--fgColor-accent);-webkit-text-decoration:none;text-decoration:none}.tabnav-btn{margin-left:var(--controlStack-medium-gap-condensed)}
1
+ .tabnav{border-bottom:var(--borderWidth-thin) solid var(--borderColor-default);margin-bottom:var(--stack-gap-normal);margin-top:0}.tabnav-tabs{display:flex;margin-bottom:calc(var(--borderWidth-thin)*-1);overflow:hidden}.tabnav::part(tablist-wrapper){border-bottom:var(--borderWidth-thin) solid var(--borderColor-default);margin-bottom:var(--stack-gap-normal)}.tabnav-tab{background-color:initial;border:var(--borderWidth-thin) solid #0000;border-bottom:0;color:var(--fgColor-muted);display:inline-block;flex-shrink:0;font-size:var(--text-body-size-medium);line-height:23px;padding:var(--base-size-8) var(--control-medium-paddingInline-spacious);-webkit-text-decoration:none;text-decoration:none;transition:color .2s cubic-bezier(.3,0,.5,1)}.tabnav-tab.selected,.tabnav-tab[aria-current]:not([aria-current=false]),.tabnav-tab[aria-selected=true]{background-color:var(--bgColor-default);border-color:var(--borderColor-default);border-radius:var(--borderRadius-medium) var(--borderRadius-medium) 0 0;color:var(--fgColor-default)}.tabnav-tab.selected .octicon,.tabnav-tab[aria-current]:not([aria-current=false]) .octicon,.tabnav-tab[aria-selected=true] .octicon{color:inherit}.tabnav-tab:hover{color:var(--fgColor-default);-webkit-text-decoration:none;text-decoration:none;transition-duration:.1s}.tabnav-tab:focus,.tabnav-tab:focus-visible{border-radius:var(--borderRadius-medium) var(--borderRadius-medium) 0 0!important;outline-offset:-6px}.tabnav-tab .octicon,.tabnav-tab:active{color:var(--fgColor-muted)}.tabnav-tab .octicon{margin-right:var(--control-small-gap)}.tabnav-tab .Counter{color:inherit;margin-left:var(--control-small-gap)}tab-container .tabnav-tab{margin-bottom:-1px}.tabnav-extra{color:var(--fgColor-muted);display:inline-block;font-size:var(--text-body-size-small);margin-left:10px;padding-top:10px}.tabnav-extra>.octicon{margin-right:2px}a.tabnav-extra:hover{color:var(--fgColor-accent);-webkit-text-decoration:none;text-decoration:none}.tabnav-btn{margin-left:var(--controlStack-medium-gap-condensed)}
@@ -3,6 +3,7 @@
3
3
  "selectors": [
4
4
  ".tabnav",
5
5
  ".tabnav-tabs",
6
+ ".tabnav::part(tablist-wrapper)",
6
7
  ".tabnav-tab",
7
8
  ".tabnav-tab.selected",
8
9
  ".tabnav-tab[aria-current]:not([aria-current=false])",
@@ -16,6 +17,7 @@
16
17
  ".tabnav-tab .octicon",
17
18
  ".tabnav-tab:active",
18
19
  ".tabnav-tab .Counter",
20
+ "tab-container .tabnav-tab",
19
21
  ".tabnav-extra",
20
22
  ".tabnav-extra>.octicon",
21
23
  "a.tabnav-extra:hover",
@@ -1 +1 @@
1
- {"version":3,"sources":["tab_nav.pcss"],"names":[],"mappings":"AAGA,QAGE,sEAAuE,CADvE,qCAAsC,CADtC,YAGF,CAEA,aACE,YAAa,CACb,8CAAiD,CACjD,aACF,CAEA,YAQE,wBAA6B,CAE7B,0CAAgB,CAAhB,eAAgB,CAJhB,0BAA2B,CAL3B,oBAAqB,CACrB,aAAc,CAEd,sCAAuC,CACvC,gBAAiB,CAFjB,uEAAwE,CAIxE,4BAAqB,CAArB,oBAAqB,CAIrB,4CAwCF,CAtCE,yGAIE,uCAAwC,CACxC,uCAAwC,CACxC,uEAAwE,CAHxE,4BAQF,CAHE,oIACE,aACF,CAGF,kBACE,4BAA6B,CAC7B,4BAAqB,CAArB,oBAAqB,CACrB,uBACF,CAEA,4CAEE,iFAAmF,CACnF,mBACF,CAMA,wCAHE,0BAMF,CAHA,qBACE,qCAEF,CAEA,qBAEE,aAAc,CADd,oCAEF,CAQF,cAKE,0BAA2B,CAJ3B,oBAAqB,CAGrB,qCAAsC,CADtC,gBAAiB,CADjB,gBAQF,CAHE,uBACE,gBACF,CAKF,qBACE,2BAA4B,CAC5B,4BAAqB,CAArB,oBACF,CAOA,YACE,oDACF","file":"tab_nav.css","sourcesContent":["/* tabnav */\n\n/* Outer wrapper */\n.tabnav {\n margin-top: 0;\n margin-bottom: var(--stack-gap-normal);\n border-bottom: var(--borderWidth-thin) solid var(--borderColor-default);\n}\n\n.tabnav-tabs {\n display: flex;\n margin-bottom: calc(var(--borderWidth-thin) * -1);\n overflow: auto;\n}\n\n.tabnav-tab {\n display: inline-block;\n flex-shrink: 0;\n padding: var(--base-size-8) var(--control-medium-paddingInline-spacious);\n font-size: var(--text-body-size-medium);\n line-height: 23px;\n color: var(--fgColor-muted);\n text-decoration: none;\n background-color: transparent;\n border: var(--borderWidth-thin) solid transparent;\n border-bottom: 0;\n transition: color 0.2s cubic-bezier(0.3, 0, 0.5, 1);\n\n &.selected,\n &[aria-selected='true'],\n &[aria-current]:not([aria-current='false']) {\n color: var(--fgColor-default);\n background-color: var(--bgColor-default); /* cover bottom border */\n border-color: var(--borderColor-default);\n border-radius: var(--borderRadius-medium) var(--borderRadius-medium) 0 0;\n\n & .octicon {\n color: inherit;\n }\n }\n\n &:hover {\n color: var(--fgColor-default);\n text-decoration: none;\n transition-duration: 0.1s;\n }\n\n &:focus,\n &:focus-visible {\n border-radius: var(--borderRadius-medium) var(--borderRadius-medium) 0 0 !important;\n outline-offset: -6px;\n }\n\n &:active {\n color: var(--fgColor-muted);\n }\n\n & .octicon {\n margin-right: var(--control-small-gap);\n color: var(--fgColor-muted);\n }\n\n & .Counter {\n margin-left: var(--control-small-gap);\n color: inherit;\n }\n}\n\n/* Tabnav extras\n**\n** Tabnav extras are non-tab elements that sit in the tabnav. Usually they're\n** inline text or links. */\n\n.tabnav-extra {\n display: inline-block;\n padding-top: 10px;\n margin-left: 10px;\n font-size: var(--text-body-size-small);\n color: var(--fgColor-muted);\n\n & > .octicon {\n margin-right: 2px;\n }\n}\n\n/* When tabnav-extra are anchors\n** stylelint-disable-next-line selector-no-qualifying-type */\na.tabnav-extra:hover {\n color: var(--fgColor-accent);\n text-decoration: none;\n}\n\n/* Tabnav buttons\n**\n** For when there are multiple buttons, space them out appropriately. Requires\n** the buttons to be floated or inline-block. */\n\n.tabnav-btn {\n margin-left: var(--controlStack-medium-gap-condensed);\n}\n"]}
1
+ {"version":3,"sources":["tab_nav.pcss"],"names":[],"mappings":"AAGA,QAGE,sEAAuE,CADvE,qCAAsC,CADtC,YAGF,CAEA,aACE,YAAa,CACb,8CAAiD,CACjD,eACF,CAEA,+BAEE,sEAAuE,CADvE,qCAEF,CAEA,YAQE,wBAA6B,CAE7B,0CAAgB,CAAhB,eAAgB,CAJhB,0BAA2B,CAL3B,oBAAqB,CACrB,aAAc,CAEd,sCAAuC,CACvC,gBAAiB,CAFjB,uEAAwE,CAIxE,4BAAqB,CAArB,oBAAqB,CAIrB,4CAwCF,CAtCE,yGAIE,uCAAwC,CACxC,uCAAwC,CACxC,uEAAwE,CAHxE,4BAQF,CAHE,oIACE,aACF,CAGF,kBACE,4BAA6B,CAC7B,4BAAqB,CAArB,oBAAqB,CACrB,uBACF,CAEA,4CAEE,iFAAmF,CACnF,mBACF,CAMA,wCAHE,0BAMF,CAHA,qBACE,qCAEF,CAEA,qBAEE,aAAc,CADd,oCAEF,CAGF,0BACE,kBACF,CAQA,cAKE,0BAA2B,CAJ3B,oBAAqB,CAGrB,qCAAsC,CADtC,gBAAiB,CADjB,gBAQF,CAHE,uBACE,gBACF,CAKF,qBACE,2BAA4B,CAC5B,4BAAqB,CAArB,oBACF,CAOA,YACE,oDACF","file":"tab_nav.css","sourcesContent":["/* tabnav */\n\n/* Outer wrapper */\n.tabnav {\n margin-top: 0;\n margin-bottom: var(--stack-gap-normal);\n border-bottom: var(--borderWidth-thin) solid var(--borderColor-default);\n}\n\n.tabnav-tabs {\n display: flex;\n margin-bottom: calc(var(--borderWidth-thin) * -1);\n overflow: hidden;\n}\n\n.tabnav::part(tablist-wrapper) {\n margin-bottom: var(--stack-gap-normal);\n border-bottom: var(--borderWidth-thin) solid var(--borderColor-default);\n}\n\n.tabnav-tab {\n display: inline-block;\n flex-shrink: 0;\n padding: var(--base-size-8) var(--control-medium-paddingInline-spacious);\n font-size: var(--text-body-size-medium);\n line-height: 23px;\n color: var(--fgColor-muted);\n text-decoration: none;\n background-color: transparent;\n border: var(--borderWidth-thin) solid transparent;\n border-bottom: 0;\n transition: color 0.2s cubic-bezier(0.3, 0, 0.5, 1);\n\n &.selected,\n &[aria-selected='true'],\n &[aria-current]:not([aria-current='false']) {\n color: var(--fgColor-default);\n background-color: var(--bgColor-default); /* cover bottom border */\n border-color: var(--borderColor-default);\n border-radius: var(--borderRadius-medium) var(--borderRadius-medium) 0 0;\n\n & .octicon {\n color: inherit;\n }\n }\n\n &:hover {\n color: var(--fgColor-default);\n text-decoration: none;\n transition-duration: 0.1s;\n }\n\n &:focus,\n &:focus-visible {\n border-radius: var(--borderRadius-medium) var(--borderRadius-medium) 0 0 !important;\n outline-offset: -6px;\n }\n\n &:active {\n color: var(--fgColor-muted);\n }\n\n & .octicon {\n margin-right: var(--control-small-gap);\n color: var(--fgColor-muted);\n }\n\n & .Counter {\n margin-left: var(--control-small-gap);\n color: inherit;\n }\n}\n\ntab-container .tabnav-tab {\n margin-bottom: -1px;\n}\n\n\n/* Tabnav extras\n**\n** Tabnav extras are non-tab elements that sit in the tabnav. Usually they're\n** inline text or links. */\n\n.tabnav-extra {\n display: inline-block;\n padding-top: 10px;\n margin-left: 10px;\n font-size: var(--text-body-size-small);\n color: var(--fgColor-muted);\n\n & > .octicon {\n margin-right: 2px;\n }\n}\n\n/* When tabnav-extra are anchors\n** stylelint-disable-next-line selector-no-qualifying-type */\na.tabnav-extra:hover {\n color: var(--fgColor-accent);\n text-decoration: none;\n}\n\n/* Tabnav buttons\n**\n** For when there are multiple buttons, space them out appropriately. Requires\n** the buttons to be floated or inline-block. */\n\n.tabnav-btn {\n margin-left: var(--controlStack-medium-gap-condensed);\n}\n"]}
@@ -10,7 +10,12 @@
10
10
  .tabnav-tabs {
11
11
  display: flex;
12
12
  margin-bottom: calc(var(--borderWidth-thin) * -1);
13
- overflow: auto;
13
+ overflow: hidden;
14
+ }
15
+
16
+ .tabnav::part(tablist-wrapper) {
17
+ margin-bottom: var(--stack-gap-normal);
18
+ border-bottom: var(--borderWidth-thin) solid var(--borderColor-default);
14
19
  }
15
20
 
16
21
  .tabnav-tab {
@@ -66,6 +71,11 @@
66
71
  }
67
72
  }
68
73
 
74
+ tab-container .tabnav-tab {
75
+ margin-bottom: -1px;
76
+ }
77
+
78
+
69
79
  /* Tabnav extras
70
80
  **
71
81
  ** Tabnav extras are non-tab elements that sit in the tabnav. Usually they're
@@ -1,13 +1,9 @@
1
- <%= tab_container_wrapper(with_panel: true, **@wrapper_arguments) do %>
2
- <%= render Primer::BaseComponent.new(**@system_arguments) do %>
3
- <%= extra if @align == :left %>
4
- <%= render Primer::BaseComponent.new(**@body_arguments) do %>
5
- <% tabs.each do |tab| %>
6
- <%= tab %>
7
- <% end %>
8
- <% end %>
9
- <%= extra if @align == :right %>
1
+ <%= render Primer::BaseComponent.new(**@system_arguments) do %>
2
+ <%= extra if @align == :left %>
3
+ <% tabs.each do |tab| %>
4
+ <%= tab %>
10
5
  <% end %>
6
+ <%= extra if @align == :right %>
11
7
  <% tabs.each do |tab| %>
12
8
  <%= tab.panel %>
13
9
  <% end %>
@@ -26,7 +26,7 @@ module Primer
26
26
  Primer::Alpha::Navigation::Tab.new(
27
27
  selected: selected,
28
28
  with_panel: true,
29
- list: true,
29
+ list: false,
30
30
  panel_id: "panel-#{id}",
31
31
  **system_arguments
32
32
  )
@@ -43,23 +43,14 @@ module Primer
43
43
 
44
44
  # @param label [String] Sets an `aria-label` that helps assistive technology users understand the purpose of the tabs.
45
45
  # @param align [Symbol] <%= one_of(Primer::TabNavHelper::EXTRA_ALIGN_OPTIONS) %> - Defaults to <%= Primer::TabNavHelper::EXTRA_ALIGN_DEFAULT %>
46
- # @param body_arguments [Hash] <%= link_to_system_arguments_docs %> for the body wrapper.
47
- # @param wrapper_arguments [Hash] <%= link_to_system_arguments_docs %> for the `TabContainer` wrapper.
48
46
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
49
47
  def initialize(label:, body_arguments: {}, wrapper_arguments: {}, **system_arguments)
50
48
  @align = EXTRA_ALIGN_DEFAULT
51
- @wrapper_arguments = wrapper_arguments
52
49
 
53
- @system_arguments = deny_tag_argument(**system_arguments)
54
- @system_arguments[:tag] = :div
50
+ @system_arguments = { **deny_tag_argument(**system_arguments), **deny_tag_argument(**wrapper_arguments) }
51
+ @system_arguments[:tag] = :"tab-container"
55
52
  @system_arguments[:classes] = tab_nav_classes(@system_arguments[:classes])
56
-
57
- @body_arguments = deny_tag_argument(**body_arguments)
58
- @body_arguments[:tag] = :ul
59
- @body_arguments[:classes] = tab_nav_body_classes(@body_arguments[:classes])
60
-
61
- @body_arguments[:role] = :tablist
62
- @body_arguments[:"aria-label"] = label
53
+ @system_arguments[:"aria-label"] = label
63
54
  end
64
55
 
65
56
  def before_render
@@ -26,6 +26,7 @@ module Primer
26
26
  # @param monospace [Boolean] If `true`, uses a monospace font for the input field.
27
27
  # @param auto_check_src [String] When provided, makes a request to the given URL whenever the contents of the text field changes. If the server responds with a non-2xx status code, the response body is used as the validation message.
28
28
  # @param leading_visual [Hash] Renders a leading visual icon before the text field's cursor. The hash will be passed to Primer's <%= link_to_component(Primer::Beta::Octicon) %> component.
29
+ # @param leading_spinner [Boolean] If `true`, a leading spinner will be included in the markup. The spinner can be shown via the `showLeadingSpinner()` JavaScript method, and hidden via `hideLeadingSpinner()`. If this argument is `true`, a leading visual must also be provided.
29
30
  # @param show_clear_button [Boolean] Whether or not to include a clear button inside the input that clears the input's contents when clicked.
30
31
  # @param clear_button_id [String] The HTML id attribute of the clear button.
31
32
  end
@@ -1 +1 @@
1
- .UnderlineNav{box-shadow:inset 0 -1px 0 var(--borderColor-muted);display:flex;min-height:var(--base-size-48);overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:auto;justify-content:space-between}.UnderlineNav .Counter{background-color:var(--bgColor-neutral-muted,var(--color-neutral-muted));color:var(--fgColor-default);margin-left:var(--control-medium-gap)}.UnderlineNav .Counter--primary{background-color:var(--bgColor-neutral-emphasis);color:var(--fgColor-onEmphasis)}.UnderlineNav-body{align-items:center;display:flex;gap:var(--control-medium-gap);list-style:none}.UnderlineNav-item{align-items:center;background-color:initial;border:0;border-radius:var(--borderRadius-medium);color:var(--fgColor-default);cursor:pointer;display:flex;font-size:var(--text-body-size-medium);line-height:30px;padding:0 var(--control-medium-paddingInline-condensed);position:relative;text-align:center;white-space:nowrap}.UnderlineNav-item:focus,.UnderlineNav-item:focus-visible,.UnderlineNav-item:hover{border-bottom-color:var(--borderColor-neutral-muted);color:var(--fgColor-default);outline-offset:-2px;-webkit-text-decoration:none;text-decoration:none;transition:border-bottom-color .12s ease-out}.UnderlineNav-item [data-content]:before{content:attr(data-content);display:block;font-weight:var(--base-text-weight-semibold);height:0;visibility:hidden}.UnderlineNav-item:before{content:"";height:100%;left:50%;min-height:48px;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:100%}@media (pointer:fine){.UnderlineNav-item:hover{background:var(--control-transparent-bgColor-hover);color:var(--fgColor-default);-webkit-text-decoration:none;text-decoration:none;transition:background .12s ease-out}}.UnderlineNav-item.selected,.UnderlineNav-item[aria-current]:not([aria-current=false]),.UnderlineNav-item[role=tab][aria-selected=true]{border-bottom-color:var(--underlineNav-borderColor-active);color:var(--fgColor-default);font-weight:var(--base-text-weight-semibold)}.UnderlineNav-item.selected:after,.UnderlineNav-item[aria-current]:not([aria-current=false]):after,.UnderlineNav-item[role=tab][aria-selected=true]:after{background:var(--underlineNav-borderColor-active);border-radius:var(--borderRadius-medium);bottom:calc(50% - 25px);content:"";height:2px;position:absolute;right:50%;transform:translate(50%,-50%);width:100%;z-index:1}.UnderlineNav--right{justify-content:flex-end}.UnderlineNav--right .UnderlineNav-actions{flex:1 1 auto}.UnderlineNav-actions{align-self:center}.UnderlineNav--full{display:block}.UnderlineNav--full .UnderlineNav-body{min-height:var(--base-size-48)}.UnderlineNav-octicon{color:var(--fgColor-muted);display:inline!important;margin-right:var(--control-medium-gap);fill:var(--fgColor-muted)}.UnderlineNav-container{display:flex;justify-content:space-between}
1
+ .UnderlineNav{box-shadow:inset 0 -1px 0 var(--borderColor-muted);display:flex;min-height:var(--base-size-48);overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:auto;justify-content:space-between}.UnderlineNav .Counter{background-color:var(--bgColor-neutral-muted,var(--color-neutral-muted));color:var(--fgColor-default);margin-left:var(--control-medium-gap)}.UnderlineNav .Counter--primary{background-color:var(--bgColor-neutral-emphasis);color:var(--fgColor-onEmphasis)}.UnderlineNav::part(tablist-wrapper){box-shadow:inset 0 -1px 0 var(--borderColor-muted);padding:var(--control-medium-gap) 0;width:100%}.UnderlineNav-body,.UnderlineNav::part(tablist){align-items:center;display:flex;gap:var(--control-medium-gap);list-style:none}.UnderlineNav-item{align-items:center;background-color:initial;border:0;border-radius:var(--borderRadius-medium);color:var(--fgColor-default);cursor:pointer;display:flex;font-size:var(--text-body-size-medium);line-height:30px;padding:0 var(--control-medium-paddingInline-condensed);position:relative;text-align:center;white-space:nowrap}.UnderlineNav-item:focus,.UnderlineNav-item:focus-visible,.UnderlineNav-item:hover{border-bottom-color:var(--borderColor-neutral-muted);color:var(--fgColor-default);outline-offset:-2px;-webkit-text-decoration:none;text-decoration:none;transition:border-bottom-color .12s ease-out}.UnderlineNav-item [data-content]:before{content:attr(data-content);display:block;font-weight:var(--base-text-weight-semibold);height:0;visibility:hidden}.UnderlineNav-item:before{content:"";height:100%;left:50%;min-height:48px;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:100%}@media (pointer:fine){.UnderlineNav-item:hover{background:var(--control-transparent-bgColor-hover);color:var(--fgColor-default);-webkit-text-decoration:none;text-decoration:none;transition:background .12s ease-out}}.UnderlineNav-item.selected,.UnderlineNav-item[aria-current]:not([aria-current=false]),.UnderlineNav-item[role=tab][aria-selected=true]{border-bottom-color:var(--underlineNav-borderColor-active);color:var(--fgColor-default);font-weight:var(--base-text-weight-semibold)}.UnderlineNav-item.selected:after,.UnderlineNav-item[aria-current]:not([aria-current=false]):after,.UnderlineNav-item[role=tab][aria-selected=true]:after{background:var(--underlineNav-borderColor-active);border-radius:var(--borderRadius-medium);bottom:calc(50% - 25px);content:"";height:2px;position:absolute;right:50%;transform:translate(50%,-50%);width:100%;z-index:1}.UnderlineNav--right{justify-content:flex-end}.UnderlineNav--right .UnderlineNav-actions{flex:1 1 auto}.UnderlineNav-actions{align-self:center}.UnderlineNav--full{display:block}.UnderlineNav--full .UnderlineNav-body{min-height:var(--base-size-48)}.UnderlineNav-octicon{color:var(--fgColor-muted);display:inline!important;margin-right:var(--control-medium-gap);fill:var(--fgColor-muted)}.UnderlineNav-container{display:flex;justify-content:space-between}
@@ -4,7 +4,9 @@
4
4
  ".UnderlineNav",
5
5
  ".UnderlineNav .Counter",
6
6
  ".UnderlineNav .Counter--primary",
7
+ ".UnderlineNav::part(tablist-wrapper)",
7
8
  ".UnderlineNav-body",
9
+ ".UnderlineNav::part(tablist)",
8
10
  ".UnderlineNav-item",
9
11
  ".UnderlineNav-item:focus",
10
12
  ".UnderlineNav-item:focus-visible",
@@ -1 +1 @@
1
- {"version":3,"sources":["underline_nav.pcss","<no source>"],"names":[],"mappings":"AAEA,cAKE,kDAAmD,CAJnD,YAAa,CACb,8BAA+B,CAC/B,eAAgB,CAChB,iBAAkB,CAElB,+BAAgC,CAChC,6BAYF,CAVE,uBAGE,wEAA0E,CAD1E,4BAA6B,CAD7B,qCAGF,CAEA,gCAEE,gDAAiD,CADjD,+BAEF,CAGF,mBAEE,kBAAmB,CADnB,YAAa,CAEb,6BAA8B,CAC9B,eACF,CAEA,mBAaE,kBAAmB,CAHnB,wBAA6B,CAC7B,QAAS,CACT,wCAAyC,CANzC,4BAA6B,CAG7B,cAAe,CAPf,YAAa,CAEb,sCAAuC,CACvC,gBAAiB,CAFjB,uDAAwD,CAFxD,iBAAkB,CAMlB,iBAAkB,CAClB,kBA8DF,CAvDE,mFAKE,oDAAqD,CAFrD,4BAA6B,CAG7B,mBAAoB,CAFpB,4BAAqB,CAArB,oBAAqB,CAGrB,4CACF,CAGA,yCAKE,0BAA2B,CAJ3B,aAAc,CAEd,4CAA6C,CAD7C,QAAS,CAET,iBAEF,CAIE,0BClEJ,WAAA,YAAA,SAAA,gBAAA,kBAAA,QAAA,4CAAA,UDkE8B,CAI5B,sBACE,yBAGE,mDAAoD,CAFpD,4BAA6B,CAC7B,4BAAqB,CAArB,oBAAqB,CAErB,mCACF,CACF,CAEA,wIAKE,0DAA2D,CAD3D,4BAA6B,CAD7B,4CAiBF,CAZE,0JAQE,iDAAkD,CAClD,wCAAyC,CALzC,uBAAwB,CAGxB,UAAW,CADX,UAAW,CALX,iBAAkB,CAElB,SAAU,CAOV,6BAA+B,CAL/B,UAAW,CAHX,SASF,CAIJ,qBACE,wBAKF,CAHE,2CACE,aACF,CAGF,sBACE,iBACF,CAEA,oBACE,aAMF,CAHE,uCACE,8BACF,CAGF,sBAGE,0BAA2B,CAF3B,wBAA0B,CAC1B,sCAAuC,CAEvC,yBACF,CAEA,wBACE,YAAa,CACb,6BACF","file":"underline_nav.css","sourcesContent":["/* UnderlineNav */\n\n.UnderlineNav {\n display: flex;\n min-height: var(--base-size-48);\n overflow-x: auto;\n overflow-y: hidden;\n box-shadow: inset 0 -1px 0 var(--borderColor-muted);\n -webkit-overflow-scrolling: auto;\n justify-content: space-between;\n\n & .Counter {\n margin-left: var(--control-medium-gap);\n color: var(--fgColor-default);\n background-color: var(--bgColor-neutral-muted, var(--color-neutral-muted));\n }\n\n & .Counter--primary {\n color: var(--fgColor-onEmphasis);\n background-color: var(--bgColor-neutral-emphasis);\n }\n}\n\n.UnderlineNav-body {\n display: flex;\n align-items: center;\n gap: var(--control-medium-gap);\n list-style: none;\n}\n\n.UnderlineNav-item {\n position: relative;\n display: flex;\n padding: 0 var(--control-medium-paddingInline-condensed);\n font-size: var(--text-body-size-medium);\n line-height: 30px;\n color: var(--fgColor-default);\n text-align: center;\n white-space: nowrap;\n cursor: pointer;\n background-color: transparent;\n border: 0;\n border-radius: var(--borderRadius-medium);\n align-items: center;\n\n &:hover,\n &:focus,\n &:focus-visible {\n color: var(--fgColor-default);\n text-decoration: none;\n border-bottom-color: var(--borderColor-neutral-muted);\n outline-offset: -2px;\n transition: border-bottom-color 0.12s ease-out;\n }\n\n /* renders a visibly hidden \"copy\" of the label in bold, reserving box space for when label becomes bold on selected */\n & [data-content]::before {\n display: block;\n height: 0;\n font-weight: var(--base-text-weight-semibold);\n visibility: hidden;\n content: attr(data-content);\n }\n\n /* increase touch target area */\n &::before {\n @mixin minTouchTarget 48px;\n }\n\n /* hover state was \"sticking\" on mobile after click */\n @media (pointer: fine) {\n &:hover {\n color: var(--fgColor-default);\n text-decoration: none;\n background: var(--control-transparent-bgColor-hover);\n transition: background 0.12s ease-out;\n }\n }\n\n &.selected,\n &[role='tab'][aria-selected='true'],\n &[aria-current]:not([aria-current='false']) {\n font-weight: var(--base-text-weight-semibold);\n color: var(--fgColor-default);\n border-bottom-color: var(--underlineNav-borderColor-active);\n\n /* current/selected underline */\n &::after {\n position: absolute;\n z-index: 1; /* raise above full-width flash banner */\n right: 50%;\n bottom: calc(50% - 25px); /* 48px total height / 2 (24px) + 1px */\n width: 100%;\n height: 2px;\n content: '';\n background: var(--underlineNav-borderColor-active);\n border-radius: var(--borderRadius-medium);\n transform: translate(50%, -50%);\n }\n }\n}\n\n.UnderlineNav--right {\n justify-content: flex-end;\n\n & .UnderlineNav-actions {\n flex: 1 1 auto;\n }\n}\n\n.UnderlineNav-actions {\n align-self: center;\n}\n\n.UnderlineNav--full {\n display: block;\n\n /* required for underline to align with additional wrapper element */\n & .UnderlineNav-body {\n min-height: var(--base-size-48);\n }\n}\n\n.UnderlineNav-octicon {\n display: inline !important;\n margin-right: var(--control-medium-gap);\n color: var(--fgColor-muted);\n fill: var(--fgColor-muted);\n}\n\n.UnderlineNav-container {\n display: flex;\n justify-content: space-between;\n}\n",null]}
1
+ {"version":3,"sources":["underline_nav.pcss","<no source>"],"names":[],"mappings":"AAEA,cAKE,kDAAmD,CAJnD,YAAa,CACb,8BAA+B,CAC/B,eAAgB,CAChB,iBAAkB,CAElB,+BAAgC,CAChC,6BAYF,CAVE,uBAGE,wEAA0E,CAD1E,4BAA6B,CAD7B,qCAGF,CAEA,gCAEE,gDAAiD,CADjD,+BAEF,CAGF,qCAEE,kDAAmD,CACnD,mCAAoC,CAFpC,UAGF,CAEA,gDAEE,kBAAmB,CADnB,YAAa,CAEb,6BAA8B,CAC9B,eACF,CAEA,mBAaE,kBAAmB,CAHnB,wBAA6B,CAC7B,QAAS,CACT,wCAAyC,CANzC,4BAA6B,CAG7B,cAAe,CAPf,YAAa,CAEb,sCAAuC,CACvC,gBAAiB,CAFjB,uDAAwD,CAFxD,iBAAkB,CAMlB,iBAAkB,CAClB,kBA8DF,CAvDE,mFAKE,oDAAqD,CAFrD,4BAA6B,CAG7B,mBAAoB,CAFpB,4BAAqB,CAArB,oBAAqB,CAGrB,4CACF,CAGA,yCAKE,0BAA2B,CAJ3B,aAAc,CAEd,4CAA6C,CAD7C,QAAS,CAET,iBAEF,CAIE,0BCxEJ,WAAA,YAAA,SAAA,gBAAA,kBAAA,QAAA,4CAAA,UDwE8B,CAI5B,sBACE,yBAGE,mDAAoD,CAFpD,4BAA6B,CAC7B,4BAAqB,CAArB,oBAAqB,CAErB,mCACF,CACF,CAEA,wIAKE,0DAA2D,CAD3D,4BAA6B,CAD7B,4CAiBF,CAZE,0JAQE,iDAAkD,CAClD,wCAAyC,CALzC,uBAAwB,CAGxB,UAAW,CADX,UAAW,CALX,iBAAkB,CAElB,SAAU,CAOV,6BAA+B,CAL/B,UAAW,CAHX,SASF,CAIJ,qBACE,wBAKF,CAHE,2CACE,aACF,CAGF,sBACE,iBACF,CAEA,oBACE,aAMF,CAHE,uCACE,8BACF,CAGF,sBAGE,0BAA2B,CAF3B,wBAA0B,CAC1B,sCAAuC,CAEvC,yBACF,CAEA,wBACE,YAAa,CACb,6BACF","file":"underline_nav.css","sourcesContent":["/* UnderlineNav */\n\n.UnderlineNav {\n display: flex;\n min-height: var(--base-size-48);\n overflow-x: auto;\n overflow-y: hidden;\n box-shadow: inset 0 -1px 0 var(--borderColor-muted);\n -webkit-overflow-scrolling: auto;\n justify-content: space-between;\n\n & .Counter {\n margin-left: var(--control-medium-gap);\n color: var(--fgColor-default);\n background-color: var(--bgColor-neutral-muted, var(--color-neutral-muted));\n }\n\n & .Counter--primary {\n color: var(--fgColor-onEmphasis);\n background-color: var(--bgColor-neutral-emphasis);\n }\n}\n\n.UnderlineNav::part(tablist-wrapper) {\n width: 100%;\n box-shadow: inset 0 -1px 0 var(--borderColor-muted);\n padding: var(--control-medium-gap) 0;\n}\n\n.UnderlineNav-body,.UnderlineNav::part(tablist) {\n display: flex;\n align-items: center;\n gap: var(--control-medium-gap);\n list-style: none;\n}\n\n.UnderlineNav-item {\n position: relative;\n display: flex;\n padding: 0 var(--control-medium-paddingInline-condensed);\n font-size: var(--text-body-size-medium);\n line-height: 30px;\n color: var(--fgColor-default);\n text-align: center;\n white-space: nowrap;\n cursor: pointer;\n background-color: transparent;\n border: 0;\n border-radius: var(--borderRadius-medium);\n align-items: center;\n\n &:hover,\n &:focus,\n &:focus-visible {\n color: var(--fgColor-default);\n text-decoration: none;\n border-bottom-color: var(--borderColor-neutral-muted);\n outline-offset: -2px;\n transition: border-bottom-color 0.12s ease-out;\n }\n\n /* renders a visibly hidden \"copy\" of the label in bold, reserving box space for when label becomes bold on selected */\n & [data-content]::before {\n display: block;\n height: 0;\n font-weight: var(--base-text-weight-semibold);\n visibility: hidden;\n content: attr(data-content);\n }\n\n /* increase touch target area */\n &::before {\n @mixin minTouchTarget 48px;\n }\n\n /* hover state was \"sticking\" on mobile after click */\n @media (pointer: fine) {\n &:hover {\n color: var(--fgColor-default);\n text-decoration: none;\n background: var(--control-transparent-bgColor-hover);\n transition: background 0.12s ease-out;\n }\n }\n\n &.selected,\n &[role='tab'][aria-selected='true'],\n &[aria-current]:not([aria-current='false']) {\n font-weight: var(--base-text-weight-semibold);\n color: var(--fgColor-default);\n border-bottom-color: var(--underlineNav-borderColor-active);\n\n /* current/selected underline */\n &::after {\n position: absolute;\n z-index: 1; /* raise above full-width flash banner */\n right: 50%;\n bottom: calc(50% - 25px); /* 48px total height / 2 (24px) + 1px */\n width: 100%;\n height: 2px;\n content: '';\n background: var(--underlineNav-borderColor-active);\n border-radius: var(--borderRadius-medium);\n transform: translate(50%, -50%);\n }\n }\n}\n\n.UnderlineNav--right {\n justify-content: flex-end;\n\n & .UnderlineNav-actions {\n flex: 1 1 auto;\n }\n}\n\n.UnderlineNav-actions {\n align-self: center;\n}\n\n.UnderlineNav--full {\n display: block;\n\n /* required for underline to align with additional wrapper element */\n & .UnderlineNav-body {\n min-height: var(--base-size-48);\n }\n}\n\n.UnderlineNav-octicon {\n display: inline !important;\n margin-right: var(--control-medium-gap);\n color: var(--fgColor-muted);\n fill: var(--fgColor-muted);\n}\n\n.UnderlineNav-container {\n display: flex;\n justify-content: space-between;\n}\n",null]}
@@ -21,7 +21,13 @@
21
21
  }
22
22
  }
23
23
 
24
- .UnderlineNav-body {
24
+ .UnderlineNav::part(tablist-wrapper) {
25
+ width: 100%;
26
+ box-shadow: inset 0 -1px 0 var(--borderColor-muted);
27
+ padding: var(--control-medium-gap) 0;
28
+ }
29
+
30
+ .UnderlineNav-body,.UnderlineNav::part(tablist) {
25
31
  display: flex;
26
32
  align-items: center;
27
33
  gap: var(--control-medium-gap);
@@ -0,0 +1 @@
1
+ tab-container.UnderlineNav{box-shadow:none;flex-direction:column}
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "alpha/underline_panels",
3
+ "selectors": [
4
+ "tab-container.UnderlineNav"
5
+ ]
6
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["underline_panels.pcss"],"names":[],"mappings":"AAAA,2BACE,eAAgB,CAChB,qBACF","file":"underline_panels.css","sourcesContent":["tab-container.UnderlineNav {\n box-shadow: none;\n flex-direction: column;\n}\n"]}
@@ -1,18 +1,16 @@
1
- <%= tab_container_wrapper(with_panel: true, **@wrapper_arguments) do %>
1
+ <%= render Primer::BaseComponent.new(**@wrapper_arguments) do %>
2
2
  <%= render Primer::BaseComponent.new(**@system_arguments) do %>
3
3
  <% if @align == :right %>
4
4
  <%= actions %>
5
5
  <% end %>
6
- <%= render body do %>
7
- <% tabs.each do |tab| %>
8
- <%= tab %>
9
- <% end %>
6
+ <% tabs.each do |tab| %>
7
+ <%= tab %>
10
8
  <% end %>
11
9
  <% if @align == :left %>
12
10
  <%= actions %>
13
11
  <% end %>
14
- <% end %>
15
- <% tabs.each do |tab| %>
16
- <%= tab.panel %>
12
+ <% tabs.each do |tab| %>
13
+ <%= tab.panel %>
14
+ <% end %>
17
15
  <% end %>
18
16
  <% end %>
@@ -0,0 +1,4 @@
1
+ tab-container.UnderlineNav {
2
+ box-shadow: none;
3
+ flex-direction: column;
4
+ }
@@ -18,7 +18,7 @@ module Primer
18
18
  Primer::Alpha::Navigation::Tab.new(
19
19
  selected: selected,
20
20
  with_panel: true,
21
- list: true,
21
+ list: false,
22
22
  icon_classes: "UnderlineNav-octicon",
23
23
  panel_id: "panel-#{id}",
24
24
  **system_arguments
@@ -43,24 +43,16 @@ module Primer
43
43
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
44
44
  def initialize(label:, align: ALIGN_DEFAULT, body_arguments: {}, wrapper_arguments: {}, **system_arguments)
45
45
  @align = fetch_or_fallback(ALIGN_OPTIONS, align, ALIGN_DEFAULT)
46
- @wrapper_arguments = wrapper_arguments
46
+ @wrapper_arguments = deny_tag_argument(**wrapper_arguments)
47
+ @wrapper_arguments[:tag] = :div
47
48
 
48
49
  @system_arguments = deny_tag_argument(**system_arguments)
49
- @system_arguments[:tag] = :div
50
+ @system_arguments[:tag] = :"tab-container"
50
51
  @system_arguments[:classes] = underline_nav_classes(@system_arguments[:classes], @align)
52
+ @system_arguments[:"aria-label"] = label
51
53
 
52
54
  @body_arguments = deny_tag_argument(**body_arguments)
53
- @body_arguments[:tag] = :ul
54
- @body_arguments[:classes] = underline_nav_body_classes(@body_arguments[:classes])
55
-
56
- @body_arguments[:role] = :tablist
57
- @body_arguments[:"aria-label"] = label
58
- end
59
-
60
- private
61
-
62
- def body
63
- Primer::BaseComponent.new(**@body_arguments)
55
+ @body_arguments[:tag] = :div
64
56
  end
65
57
  end
66
58
  end
@@ -14,6 +14,11 @@ module Primer
14
14
  # Either `aria-label` or `aria-description` will be used for the `Tooltip` text, depending on which one is present.
15
15
  # Either `aria-label` or `aria-description` will be used for the `Tooltip` text, depending on which one is present.
16
16
  # [Learn more about best functional image practices (WAI Images)](https://www.w3.org/WAI/tutorials/images/functional)
17
+ #
18
+ # Additional markup is required if setting the `tag` argument to either `:a` or `:summary`.
19
+ #
20
+ # * `:a` requires you to pass in an `href` attribute
21
+ # * `:summary` requires you to wrap the component in a `<details>` element
17
22
  class IconButton < Primer::Component
18
23
  status :beta
19
24
 
@@ -101,6 +101,7 @@ module Primer
101
101
  # @param format_style [Symbol] The format the display should take. <%= one_of(Primer::Beta::RelativeTime::FORMAT_STYLE_OPTIONS) %>
102
102
  # @param lang [string] The language to use.
103
103
  # @param title [string] Provide a custom title to the element.
104
+ # @param no_title [Boolean] Removes the `title` attribute provided on the element by default.
104
105
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
105
106
  def initialize(
106
107
  datetime:,
@@ -120,6 +121,7 @@ module Primer
120
121
  format_style: nil,
121
122
  lang: nil,
122
123
  title: nil,
124
+ no_title: false,
123
125
  **system_arguments
124
126
  )
125
127
  @system_arguments = deny_tag_argument(**system_arguments)
@@ -137,6 +139,7 @@ module Primer
137
139
  @system_arguments[:threshold] = threshold if threshold.present?
138
140
  @system_arguments[:precision] = precision if precision.present?
139
141
  @system_arguments[:title] = title if title.present?
142
+ @system_arguments[:"no-title"] = no_title if no_title
140
143
  @system_arguments[:lang] = lang if lang.present?
141
144
  @system_arguments[:format] = fetch_or_fallback(FORMAT_OPTIONS, format, FORMAT_DEFAULT) if format.present?
142
145
  @system_arguments[:"format-style"] = format_style if format_style.present?
@@ -2,3 +2,6 @@
2
2
  <circle cx="8" cy="8" r="7" stroke="currentColor" stroke-opacity="0.25" stroke-width="2" vector-effect="non-scaling-stroke" fill="none" />
3
3
  <path d="M15 8a7.002 7.002 0 00-7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" vector-effect="non-scaling-stroke" />
4
4
  <% end %>
5
+ <% if no_aria_label? %>
6
+ <span class="sr-only"><%= @sr_text %></span>
7
+ <% end %>
@@ -12,6 +12,7 @@ module Primer
12
12
  DEFAULT_SIZE => 32,
13
13
  :large => 64
14
14
  }.freeze
15
+ DEFAULT_SR_TEXT = "Loading"
15
16
 
16
17
  SIZE_OPTIONS = SIZE_MAPPINGS.keys
17
18
 
@@ -22,7 +23,7 @@ module Primer
22
23
  # @param size [Symbol] <%= one_of(Primer::Beta::Spinner::SIZE_MAPPINGS) %>
23
24
  # @param style [String] Custom element styles.
24
25
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
25
- def initialize(size: DEFAULT_SIZE, style: DEFAULT_STYLE, **system_arguments)
26
+ def initialize(size: DEFAULT_SIZE, style: DEFAULT_STYLE, sr_text: DEFAULT_SR_TEXT, **system_arguments)
26
27
  @system_arguments = deny_tag_argument(**system_arguments)
27
28
  @system_arguments[:tag] = :svg
28
29
  @system_arguments[:style] ||= style
@@ -31,6 +32,19 @@ module Primer
31
32
  @system_arguments[:height] = SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)]
32
33
  @system_arguments[:viewBox] = "0 0 16 16"
33
34
  @system_arguments[:fill] = :none
35
+ @system_arguments[:aria] ||= {}
36
+ @sr_text = sr_text
37
+
38
+ if no_aria_label?
39
+ @system_arguments[:aria][:hidden] = true
40
+ else
41
+ @system_arguments[:role] = "img"
42
+ end
43
+ end
44
+
45
+ def no_aria_label?
46
+ !@system_arguments[:aria][:label] && !@system_arguments[:"aria-label"] &&
47
+ !@system_arguments[:aria][:labelledby] && !@system_arguments[:"aria-labelledby"]
34
48
  end
35
49
  end
36
50
  end
@@ -29,7 +29,7 @@ module Primer
29
29
 
30
30
  def render?
31
31
  # no slot provided
32
- raise ArgumentError, "You have to provide either rows, columns or boxes as a slot" if rows.empty? && columns.empty? && boxes.empty?
32
+ return false if rows.empty? && columns.empty? && boxes.empty?
33
33
 
34
34
  if [rows, columns, boxes].count { |arr| !arr.empty? } == 1
35
35
  # only rows or columns or boxes are used
@@ -12,6 +12,7 @@
12
12
  @import "./alpha/button_marketing.pcss";
13
13
  @import "./alpha/toggle_switch.pcss";
14
14
  @import "./alpha/underline_nav.pcss";
15
+ @import "./alpha/underline_panels.pcss";
15
16
  @import "./alpha/segmented_control.pcss";
16
17
  @import "./alpha/menu.pcss";
17
18
 
@@ -7,17 +7,20 @@ module Primer
7
7
  class TextFieldInput < Input
8
8
  attr_reader(
9
9
  *%i[
10
- name label show_clear_button leading_visual clear_button_id
10
+ name label show_clear_button leading_visual leading_spinner clear_button_id
11
11
  visually_hide_label inset monospace field_wrap_classes auto_check_src
12
12
  ]
13
13
  )
14
14
 
15
+ alias leading_spinner? leading_spinner
16
+
15
17
  def initialize(name:, label:, **system_arguments)
16
18
  @name = name
17
19
  @label = label
18
20
 
19
21
  @show_clear_button = system_arguments.delete(:show_clear_button)
20
22
  @leading_visual = system_arguments.delete(:leading_visual)
23
+ @leading_spinner = !!system_arguments.delete(:leading_spinner)
21
24
  @clear_button_id = system_arguments.delete(:clear_button_id)
22
25
  @inset = system_arguments.delete(:inset)
23
26
  @monospace = system_arguments.delete(:monospace)
@@ -30,6 +33,10 @@ module Primer
30
33
  )
31
34
  end
32
35
 
36
+ if @leading_spinner && !@leading_visual
37
+ raise ArgumentError, "text fields that request a leading spinner must also specify a leading visual"
38
+ end
39
+
33
40
  super(**system_arguments)
34
41
 
35
42
  add_input_data(:target, "primer-text-field.inputElement #{system_arguments.dig(:data, :target) || ''}")