primer_view_components 0.18.1 → 0.19.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/app/assets/javascripts/primer_view_components.js +1 -1
  4. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  5. data/app/assets/styles/primer_view_components.css +1 -1
  6. data/app/assets/styles/primer_view_components.css.map +1 -1
  7. data/app/components/primer/alpha/action_bar_element.js +16 -10
  8. data/app/components/primer/alpha/action_bar_element.ts +10 -4
  9. data/app/components/primer/alpha/action_menu/action_menu_element.js +16 -20
  10. data/app/components/primer/alpha/action_menu/action_menu_element.ts +4 -1
  11. data/app/components/primer/alpha/banner.html.erb +1 -2
  12. data/app/components/primer/alpha/banner.rb +21 -0
  13. data/app/components/primer/alpha/dialog.rb +0 -1
  14. data/app/components/primer/alpha/modal_dialog.js +11 -10
  15. data/app/components/primer/alpha/modal_dialog.ts +3 -0
  16. data/app/components/primer/alpha/segmented_control.js +2 -3
  17. data/app/components/primer/alpha/toggle_switch.js +2 -2
  18. data/app/components/primer/alpha/toggle_switch.ts +2 -2
  19. data/app/components/primer/alpha/tool_tip.js +6 -7
  20. data/app/components/primer/alpha/tool_tip.ts +1 -0
  21. data/app/components/primer/beta/button.css +1 -1
  22. data/app/components/primer/beta/button.css.json +0 -1
  23. data/app/components/primer/beta/button.css.map +1 -1
  24. data/app/components/primer/beta/button.pcss +1 -5
  25. data/app/components/primer/beta/button_group.rb +53 -1
  26. data/app/components/primer/beta/clipboard_copy.ts +1 -1
  27. data/app/components/primer/beta/flash.rb +3 -1
  28. data/app/components/primer/beta/nav_list.js +8 -10
  29. data/app/components/primer/beta/nav_list.ts +2 -0
  30. data/app/components/primer/beta/nav_list_group_element.js +2 -3
  31. data/app/components/primer/dialog_helper.js +39 -20
  32. data/app/components/primer/dialog_helper.ts +40 -14
  33. data/app/components/primer/focus_group.js +9 -12
  34. data/app/components/primer/focus_group.ts +1 -1
  35. data/lib/primer/deprecations.yml +5 -0
  36. data/lib/primer/forms/primer_multi_input.js +2 -3
  37. data/lib/primer/forms/primer_text_field.js +2 -4
  38. data/lib/primer/view_components/version.rb +2 -2
  39. data/previews/primer/alpha/dialog_preview/dialog_inside_overlay.html.erb +13 -2
  40. data/previews/primer/alpha/dialog_preview.rb +1 -0
  41. data/previews/primer/beta/button_group_preview/with_menu_button.html.erb +7 -0
  42. data/previews/primer/beta/button_group_preview.rb +6 -0
  43. data/static/arguments.json +23 -1
  44. data/static/audited_at.json +1 -0
  45. data/static/constants.json +3 -0
  46. data/static/info_arch.json +69 -5
  47. data/static/previews.json +15 -2
  48. data/static/statuses.json +2 -1
  49. metadata +4 -4
  50. data/previews/primer/beta/button_group_preview/action_menus.html.erb +0 -8
@@ -1 +1 @@
1
- {"version":3,"sources":["button.pcss","<no source>","../../../../lib/postcss_mixins/focusOutlineOnEmphasis.pcss"],"names":[],"mappings":"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","file":"button.css","sourcesContent":["/* CSS for Button */\n\n/* temporary, pre primitives release */\n:root {\n --duration-fast: 80ms;\n --easing-easeInOut: cubic-bezier(0.65, 0, 0.35, 1);\n}\n\n/* base button */\n.Button {\n position: relative;\n font-size: var(--text-body-size-medium);\n font-weight: var(--base-text-weight-medium);\n cursor: pointer;\n user-select: none;\n background-color: transparent;\n border: var(--borderWidth-thin) solid;\n border-color: transparent;\n border-radius: var(--borderRadius-medium);\n color: var(--button-default-fgColor-rest);\n transition: var(--duration-fast) var(--easing-easeInOut);\n transition-property: color, fill, background-color, border-color;\n text-align: center;\n height: var(--control-medium-size);\n padding: 0 var(--control-medium-paddingInline-normal);\n display: inline-flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n gap: var(--base-size-4);\n min-width: max-content;\n\n /* mobile friendly sizing */\n @media (pointer: coarse) {\n &::before {\n @mixin minTouchTarget 48px, 48px;\n }\n }\n\n /* base states */\n\n &:hover {\n transition-duration: var(--duration-fast);\n }\n\n &:active {\n transition: none;\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n cursor: not-allowed;\n box-shadow: none;\n }\n}\n\na.Button,\nsummary.Button {\n &:hover {\n text-decoration: none;\n }\n}\n\n/* wrap grid content to allow trailingAction to lock-right */\n.Button-content {\n flex: 1 0 auto;\n display: grid;\n grid-template-areas: 'leadingVisual text trailingVisual';\n grid-template-columns: min-content minmax(0, auto) min-content;\n align-items: center;\n place-content: center;\n\n /* padding-bottom: 1px; optical alignment for firefox */\n\n & > :not(:last-child) {\n margin-right: var(--control-medium-gap);\n }\n}\n\n/* center child elements for fullWidth */\n.Button-content--alignStart {\n justify-content: start;\n}\n\n/* button child elements */\n\n/* align svg */\n.Button-visual {\n display: flex;\n pointer-events: none; /* allow click handler to work, avoiding visuals */\n\n & .Counter {\n color: inherit;\n background-color: var(--buttonCounter-default-bgColor-rest);\n }\n}\n\n.Button-label {\n grid-area: text;\n white-space: nowrap;\n line-height: var(--text-body-lineHeight-medium);\n}\n\n.Button-leadingVisual {\n grid-area: leadingVisual;\n}\n\n.Button-leadingVisual svg {\n fill: currentcolor;\n}\n\n.Button-trailingVisual {\n grid-area: trailingVisual;\n}\n\n.Button-trailingAction {\n margin-right: calc(var(--base-size-4) * -1);\n}\n\n/* sizes */\n\n.Button--small {\n font-size: var(--text-body-size-small);\n height: var(--control-small-size);\n padding: 0 var(--control-small-paddingInline-condensed);\n gap: var(--control-small-gap);\n\n & .Button-label {\n line-height: var(--text-body-lineHeight-small);\n }\n\n & .Button-content {\n & > :not(:last-child) {\n margin-right: var(--control-small-gap);\n }\n }\n}\n\n.Button--large {\n height: var(--control-large-size);\n padding: 0 var(--control-large-paddingInline-spacious);\n gap: var(--control-large-gap);\n\n & .Button-label {\n line-height: var(--text-body-lineHeight-large);\n }\n\n & .Button-content {\n & > :not(:last-child) {\n margin-right: var(--control-large-gap);\n }\n }\n}\n\n.Button--fullWidth {\n width: 100%;\n}\n\n/* variants */\n\n/* primary */\n.Button--primary {\n color: var(--button-primary-fgColor-rest);\n fill: var(--button-primary-iconColor-rest);\n background-color: var(--button-primary-bgColor-rest);\n border-color: var(--button-primary-borderColor-rest);\n box-shadow: var(--shadow-resting-small, var(--color-btn-primary-shadow));\n\n &:hover:not(:disabled, .Button--inactive) {\n background-color: var(--button-primary-bgColor-hover);\n border-color: var(--button-primary-borderColor-hover);\n }\n\n /* fallback :focus state */\n &:focus {\n @mixin focusOutlineOnEmphasis;\n\n /* remove fallback :focus if :focus-visible is supported */\n &:not(:focus-visible) {\n outline: solid 1px transparent;\n box-shadow: none;\n }\n }\n\n /* default focus state */\n &:focus-visible {\n @mixin focusOutlineOnEmphasis;\n }\n\n &:active:not(:disabled),\n &[aria-pressed='true'] {\n background-color: var(--button-primary-bgColor-active);\n box-shadow: var(--button-primary-shadow-selected);\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n color: var(--button-primary-fgColor-disabled);\n background-color: var(--button-primary-bgColor-disabled);\n border-color: var(--button-primary-borderColor-disabled);\n fill: var(--button-primary-fgColor-disabled);\n }\n\n & .Counter {\n color: inherit;\n background-color: var(--buttonCounter-primary-bgColor-rest);\n }\n}\n\n/* default (secondary) */\n.Button--secondary {\n color: var(--button-default-fgColor-rest);\n fill: var(--fgColor-muted); /* help this */\n background-color: var(--button-default-bgColor-rest);\n border-color: var(--button-default-borderColor-rest);\n box-shadow: var(--button-default-shadow-resting), var(--button-default-shadow-inset);\n\n &:hover:not(:disabled, .Button--inactive) {\n background-color: var(--button-default-bgColor-hover);\n border-color: var(--button-default-borderColor-hover);\n }\n\n &:active:not(:disabled) {\n background-color: var(--button-default-bgColor-active);\n border-color: var(--button-default-borderColor-active);\n }\n\n &[aria-pressed='true'] {\n background-color: var(--button-default-bgColor-selected);\n box-shadow: var(--shadow-inset);\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n color: var(--control-fgColor-disabled);\n background-color: var(--button-default-bgColor-disabled);\n border-color: var(--button-default-borderColor-disabled);\n fill: var(--control-fgColor-disabled);\n }\n}\n\n.Button--invisible {\n color: var(--button-default-fgColor-rest);\n\n &.Button--iconOnly {\n color: var(--button-invisible-iconColor-rest, var(--color-fg-muted));\n }\n\n &:hover:not(:disabled, .Button--inactive) {\n background-color: var(--button-invisible-bgColor-hover);\n\n & .Button-visual {\n color: var(--button-invisible-iconColor-hover, var(--color-fg-default));\n }\n }\n\n &[aria-pressed='true'],\n &:active:not(:disabled) {\n background-color: var(--button-invisible-bgColor-active);\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n color: var(--button-invisible-fgColor-disabled);\n background-color: var(--button-invisible-bgColor-disabled);\n border-color: var(--button-invisible-borderColor-disabled);\n fill: var(--button-invisible-fgColor-disabled);\n }\n\n /* if button has no visuals, use link blue for text */\n &.Button--invisible-noVisuals .Button-label {\n color: var(--button-invisible-fgColor-rest);\n }\n\n & .Button-visual {\n color: var(--button-invisible-iconColor-rest, var(--color-fg-muted));\n\n & .Counter {\n color: var(--fgColor-default);\n }\n }\n}\n\n.Button--link {\n color: var(--fgColor-link);\n fill: var(--fgColor-link);\n display: inline-block;\n font-size: inherit;\n border: none;\n height: unset;\n padding: 0;\n\n &:hover:not(:disabled, .Button--inactive) {\n text-decoration: underline;\n }\n\n &:focus-visible,\n &:focus {\n outline-offset: 2px;\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n color: var(--control-fgColor-disabled);\n background-color: transparent;\n border-color: transparent;\n fill: var(--control-fgColor-disabled);\n }\n}\n\n/* danger */\n.Button--danger {\n color: var(--button-danger-fgColor-rest);\n fill: var(--button-danger-iconColor-rest);\n background-color: var(--button-danger-bgColor-rest);\n border-color: var(--button-danger-borderColor-rest);\n box-shadow: var(--button-default-shadow-resting), var(--button-default-shadow-inset);\n\n &:hover:not(:disabled, .Button--inactive) {\n color: var(--button-danger-fgColor-hover);\n fill: var(--button-danger-fgColor-hover);\n background-color: var(--button-danger-bgColor-hover);\n border-color: var(--button-danger-borderColor-hover);\n box-shadow: var(--shadow-resting-small);\n\n & .Counter {\n color: var(--buttonCounter-danger-fgColor-hover);\n background-color: var(--buttonCounter-danger-bgColor-hover);\n }\n }\n\n &:active:not(:disabled),\n &[aria-pressed='true'] {\n color: var(--button-danger-fgColor-active);\n fill: var(--button-danger-fgColor-active);\n background-color: var(--button-danger-bgColor-active);\n border-color: var(--button-danger-borderColor-active);\n box-shadow: var(--button-danger-shadow-selected);\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n color: var(--button-danger-fgColor-disabled);\n fill: var(--button-danger-fgColor-disabled);\n background-color: var(--button-danger-bgColor-disabled);\n border-color: var(--button-default-borderColor-disabled);\n\n & .Counter {\n color: var(--buttonCounter-danger-fgColor-disabled);\n background-color: var(--buttonCounter-danger-bgColor-disabled);\n }\n }\n\n & .Counter {\n color: var(--buttonCounter-danger-fgColor-rest);\n background-color: var(--buttonCounter-danger-bgColor-rest);\n }\n}\n\n.Button--iconOnly {\n display: inline-grid;\n place-content: center;\n padding: unset;\n width: var(--control-medium-size);\n\n &.Button--small {\n width: var(--control-small-size);\n }\n\n &.Button--large {\n width: var(--control-large-size);\n }\n}\n\n/* `disabled` takes precedence over `inactive` */\n.Button--inactive:not([aria-disabled='true'], :disabled) {\n background-color: var(--button-inactive-bgColor);\n border: 0;\n color: var(--button-inactive-fgColor);\n cursor: default;\n}\n",null,"/* outline with fg box-shadow for buttons */\n@define-mixin focusOutlineOnEmphasis $outlineOffset: -2px, $outlineColor: var(--focus-outlineColor) {\n outline: 2px solid $outlineColor;\n outline-offset: $outlineOffset;\n box-shadow: inset 0 0 0 3px var(--fgColor-onEmphasis);\n}\n"]}
1
+ {"version":3,"sources":["button.pcss","<no source>","../../../../lib/postcss_mixins/focusOutlineOnEmphasis.pcss"],"names":[],"mappings":"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","file":"button.css","sourcesContent":["/* CSS for Button */\n\n/* temporary, pre primitives release */\n:root {\n --duration-fast: 80ms;\n --easing-easeInOut: cubic-bezier(0.65, 0, 0.35, 1);\n}\n\n/* base button */\n.Button {\n position: relative;\n font-size: var(--text-body-size-medium);\n font-weight: var(--base-text-weight-medium);\n cursor: pointer;\n user-select: none;\n background-color: transparent;\n border: var(--borderWidth-thin) solid;\n border-color: transparent;\n border-radius: var(--borderRadius-medium);\n color: var(--button-default-fgColor-rest);\n transition: var(--duration-fast) var(--easing-easeInOut);\n transition-property: color, fill, background-color, border-color;\n text-align: center;\n height: var(--control-medium-size);\n padding: 0 var(--control-medium-paddingInline-normal);\n display: inline-flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n gap: var(--base-size-4);\n min-width: max-content;\n\n /* mobile friendly sizing */\n @media (pointer: coarse) {\n &::before {\n @mixin minTouchTarget 48px, 48px;\n }\n }\n\n /* base states */\n\n &:hover {\n transition-duration: var(--duration-fast);\n }\n\n &:active {\n transition: none;\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n cursor: not-allowed;\n box-shadow: none;\n }\n}\n\na.Button,\nsummary.Button {\n &:hover {\n text-decoration: none;\n }\n}\n\n/* wrap grid content to allow trailingAction to lock-right */\n.Button-content {\n flex: 1 0 auto;\n display: grid;\n grid-template-areas: 'leadingVisual text trailingVisual';\n grid-template-columns: min-content minmax(0, auto) min-content;\n align-items: center;\n place-content: center;\n\n /* padding-bottom: 1px; optical alignment for firefox */\n\n & > :not(:last-child) {\n margin-right: var(--control-medium-gap);\n }\n}\n\n/* center child elements for fullWidth */\n.Button-content--alignStart {\n justify-content: start;\n}\n\n/* button child elements */\n\n/* align svg */\n.Button-visual {\n display: flex;\n pointer-events: none; /* allow click handler to work, avoiding visuals */\n\n & .Counter {\n color: inherit;\n background-color: var(--buttonCounter-default-bgColor-rest);\n }\n}\n\n.Button-label {\n grid-area: text;\n white-space: nowrap;\n line-height: var(--text-body-lineHeight-medium);\n}\n\n.Button-leadingVisual {\n grid-area: leadingVisual;\n}\n\n.Button-leadingVisual svg {\n fill: currentcolor;\n}\n\n.Button-trailingVisual {\n grid-area: trailingVisual;\n}\n\n.Button-trailingAction {\n margin-right: calc(var(--base-size-4) * -1);\n}\n\n/* sizes */\n\n.Button--small {\n font-size: var(--text-body-size-small);\n height: var(--control-small-size);\n padding: 0 var(--control-small-paddingInline-condensed);\n gap: var(--control-small-gap);\n\n & .Button-label {\n line-height: var(--text-body-lineHeight-small);\n }\n\n & .Button-content {\n & > :not(:last-child) {\n margin-right: var(--control-small-gap);\n }\n }\n}\n\n.Button--large {\n height: var(--control-large-size);\n padding: 0 var(--control-large-paddingInline-spacious);\n gap: var(--control-large-gap);\n\n & .Button-label {\n line-height: var(--text-body-lineHeight-large);\n }\n\n & .Button-content {\n & > :not(:last-child) {\n margin-right: var(--control-large-gap);\n }\n }\n}\n\n.Button--fullWidth {\n width: 100%;\n}\n\n/* variants */\n\n/* primary */\n.Button--primary {\n color: var(--button-primary-fgColor-rest);\n fill: var(--button-primary-iconColor-rest);\n background-color: var(--button-primary-bgColor-rest);\n border-color: var(--button-primary-borderColor-rest);\n box-shadow: var(--shadow-resting-small, var(--color-btn-primary-shadow));\n\n &:hover:not(:disabled, .Button--inactive) {\n background-color: var(--button-primary-bgColor-hover);\n border-color: var(--button-primary-borderColor-hover);\n }\n\n /* fallback :focus state */\n &:focus {\n @mixin focusOutlineOnEmphasis;\n\n /* remove fallback :focus if :focus-visible is supported */\n &:not(:focus-visible) {\n outline: solid 1px transparent;\n box-shadow: none;\n }\n }\n\n /* default focus state */\n &:focus-visible {\n @mixin focusOutlineOnEmphasis;\n }\n\n &:active:not(:disabled),\n &[aria-pressed='true'] {\n background-color: var(--button-primary-bgColor-active);\n box-shadow: var(--button-primary-shadow-selected);\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n color: var(--button-primary-fgColor-disabled);\n background-color: var(--button-primary-bgColor-disabled);\n border-color: var(--button-primary-borderColor-disabled);\n fill: var(--button-primary-fgColor-disabled);\n }\n\n & .Counter {\n color: inherit;\n background-color: var(--buttonCounter-primary-bgColor-rest);\n }\n}\n\n/* default (secondary) */\n.Button--secondary {\n color: var(--button-default-fgColor-rest);\n fill: var(--fgColor-muted); /* help this */\n background-color: var(--button-default-bgColor-rest);\n border-color: var(--button-default-borderColor-rest);\n box-shadow: var(--button-default-shadow-resting), var(--button-default-shadow-inset);\n\n &:hover:not(:disabled, .Button--inactive) {\n background-color: var(--button-default-bgColor-hover);\n border-color: var(--button-default-borderColor-hover);\n }\n\n &:active:not(:disabled) {\n background-color: var(--button-default-bgColor-active);\n border-color: var(--button-default-borderColor-active);\n }\n\n &[aria-pressed='true'] {\n background-color: var(--button-default-bgColor-selected);\n box-shadow: var(--shadow-inset);\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n color: var(--control-fgColor-disabled);\n background-color: var(--button-default-bgColor-disabled);\n border-color: var(--button-default-borderColor-disabled);\n fill: var(--control-fgColor-disabled);\n }\n}\n\n.Button--invisible {\n color: var(--button-default-fgColor-rest);\n\n &.Button--iconOnly {\n color: var(--button-invisible-iconColor-rest, var(--color-fg-muted));\n }\n\n &:hover:not(:disabled, .Button--inactive) {\n background-color: var(--control-transparent-bgColor-hover, var(--color-action-list-item-default-hover-bg));\n }\n\n &[aria-pressed='true'],\n &:active:not(:disabled) {\n background-color: var(--button-invisible-bgColor-active);\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n color: var(--button-invisible-fgColor-disabled);\n background-color: var(--button-invisible-bgColor-disabled);\n border-color: var(--button-invisible-borderColor-disabled);\n fill: var(--button-invisible-fgColor-disabled);\n }\n\n /* if button has no visuals, use link blue for text */\n &.Button--invisible-noVisuals .Button-label {\n color: var(--button-invisible-fgColor-rest);\n }\n\n & .Button-visual {\n color: var(--button-invisible-iconColor-rest, var(--color-fg-muted));\n\n & .Counter {\n color: var(--fgColor-default);\n }\n }\n}\n\n.Button--link {\n color: var(--fgColor-link);\n fill: var(--fgColor-link);\n display: inline-block;\n font-size: inherit;\n border: none;\n height: unset;\n padding: 0;\n\n &:hover:not(:disabled, .Button--inactive) {\n text-decoration: underline;\n }\n\n &:focus-visible,\n &:focus {\n outline-offset: 2px;\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n color: var(--control-fgColor-disabled);\n background-color: transparent;\n border-color: transparent;\n fill: var(--control-fgColor-disabled);\n }\n}\n\n/* danger */\n.Button--danger {\n color: var(--button-danger-fgColor-rest);\n fill: var(--button-danger-iconColor-rest);\n background-color: var(--button-danger-bgColor-rest);\n border-color: var(--button-danger-borderColor-rest);\n box-shadow: var(--button-default-shadow-resting), var(--button-default-shadow-inset);\n\n &:hover:not(:disabled, .Button--inactive) {\n color: var(--button-danger-fgColor-hover);\n fill: var(--button-danger-fgColor-hover);\n background-color: var(--button-danger-bgColor-hover);\n border-color: var(--button-danger-borderColor-hover);\n box-shadow: var(--shadow-resting-small);\n\n & .Counter {\n color: var(--buttonCounter-danger-fgColor-hover);\n background-color: var(--buttonCounter-danger-bgColor-hover);\n }\n }\n\n &:active:not(:disabled),\n &[aria-pressed='true'] {\n color: var(--button-danger-fgColor-active);\n fill: var(--button-danger-fgColor-active);\n background-color: var(--button-danger-bgColor-active);\n border-color: var(--button-danger-borderColor-active);\n box-shadow: var(--button-danger-shadow-selected);\n }\n\n &:disabled,\n &[aria-disabled='true'] {\n color: var(--button-danger-fgColor-disabled);\n fill: var(--button-danger-fgColor-disabled);\n background-color: var(--button-danger-bgColor-disabled);\n border-color: var(--button-default-borderColor-disabled);\n\n & .Counter {\n color: var(--buttonCounter-danger-fgColor-disabled);\n background-color: var(--buttonCounter-danger-bgColor-disabled);\n }\n }\n\n & .Counter {\n color: var(--buttonCounter-danger-fgColor-rest);\n background-color: var(--buttonCounter-danger-bgColor-rest);\n }\n}\n\n.Button--iconOnly {\n display: inline-grid;\n place-content: center;\n padding: unset;\n width: var(--control-medium-size);\n\n &.Button--small {\n width: var(--control-small-size);\n }\n\n &.Button--large {\n width: var(--control-large-size);\n }\n}\n\n/* `disabled` takes precedence over `inactive` */\n.Button--inactive:not([aria-disabled='true'], :disabled) {\n background-color: var(--button-inactive-bgColor);\n border: 0;\n color: var(--button-inactive-fgColor);\n cursor: default;\n}\n",null,"/* outline with fg box-shadow for buttons */\n@define-mixin focusOutlineOnEmphasis $outlineOffset: -2px, $outlineColor: var(--focus-outlineColor) {\n outline: 2px solid $outlineColor;\n outline-offset: $outlineOffset;\n box-shadow: inset 0 0 0 3px var(--fgColor-onEmphasis);\n}\n"]}
@@ -247,11 +247,7 @@ summary.Button {
247
247
  }
248
248
 
249
249
  &:hover:not(:disabled, .Button--inactive) {
250
- background-color: var(--button-invisible-bgColor-hover);
251
-
252
- & .Button-visual {
253
- color: var(--button-invisible-iconColor-hover, var(--color-fg-default));
254
- }
250
+ background-color: var(--control-transparent-bgColor-hover, var(--color-action-list-item-default-hover-bg));
255
251
  }
256
252
 
257
253
  &[aria-pressed='true'],
@@ -14,6 +14,13 @@ module Primer
14
14
  # def with_button(icon: nil, **system_arguments)
15
15
  # end
16
16
 
17
+ # @!parse
18
+ # # Adds a button that activates a menu when clicked.
19
+ # #
20
+ # # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Beta::ButtonGroup::MenuButton) %>.
21
+ # def with_menu_button(**system_arguments)
22
+ # end
23
+
17
24
  # @!parse
18
25
  # # Adds a <%= link_to_component(Primer::Beta::ClipboardCopyButton) %>.
19
26
  # #
@@ -21,7 +28,7 @@ module Primer
21
28
  # def with_clipboard_copy_button(**system_arguments)
22
29
  # end
23
30
 
24
- # List of buttons to be rendered. Add buttons via the `#with_button` and `#with_clipboard_copy_button` methods (see below).
31
+ # List of buttons to be rendered. Add buttons via the `#with_button`, `#with_menu_button`, and `#with_clipboard_copy_button` methods (see below).
25
32
  renders_many :buttons, types: {
26
33
  button: {
27
34
  renders: lambda { |icon: nil, **kwargs|
@@ -38,6 +45,14 @@ module Primer
38
45
  as: :button
39
46
  },
40
47
 
48
+ menu_button: {
49
+ renders: lambda { |**system_arguments|
50
+ MenuButton.new(**system_arguments)
51
+ },
52
+
53
+ as: :menu_button
54
+ },
55
+
41
56
  clipboard_copy_button: {
42
57
  renders: lambda { |**kwargs|
43
58
  kwargs[:size] = @size
@@ -67,6 +82,43 @@ module Primer
67
82
  def render?
68
83
  buttons.any?
69
84
  end
85
+
86
+ # Renders a button in a <%= link_to_component(Primer::Beta::ButtonGroup) %> that displays an <%= link_to_component(Primer::Alpha::ActionMenu) %> when clicked.
87
+ # This component should not be used outside of a `ButtonGroup` context.
88
+ #
89
+ # This component yields both the button and the list to the block when rendered.
90
+ #
91
+ # ```erb
92
+ # <%= render(Primer::Beta::ButtonGroup.new) do |group| %>
93
+ # <% group.with_menu_button do |menu, button| %>
94
+ # <% menu.with_item(label: "Item 1") %>
95
+ # <% button.with_trailing_visual_icon(icon: "triangle-down") %>
96
+ # <% end %>
97
+ # <% end %>
98
+ # ```
99
+ #
100
+ class MenuButton < Primer::Component
101
+ # @param menu_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionMenu) %>.
102
+ # @param button_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Beta::Button) %> or <%= link_to_component(Primer::Beta::IconButton) %>, depending on the value of the `icon:` argument.
103
+ def initialize(menu_arguments: {}, button_arguments: {})
104
+ @menu = Primer::Alpha::ActionMenu.new(**menu_arguments)
105
+ @button = @menu.with_show_button(icon: "triangle-down", **button_arguments)
106
+ end
107
+
108
+ def render_in(view_context, &block)
109
+ super(view_context) do
110
+ block.call(@menu, @button)
111
+ end
112
+ end
113
+
114
+ def before_render
115
+ content
116
+ end
117
+
118
+ def call
119
+ render(@menu)
120
+ end
121
+ end
70
122
  end
71
123
  end
72
124
  end
@@ -50,6 +50,6 @@ document.addEventListener('clipboard-copy', ({target}) => {
50
50
  setTimeout(() => {
51
51
  showCopy(target)
52
52
  clipboardCopyElementTimers.delete(target)
53
- }, CLIPBOARD_COPY_TIMER_DURATION)
53
+ }, CLIPBOARD_COPY_TIMER_DURATION),
54
54
  )
55
55
  })
@@ -2,9 +2,11 @@
2
2
 
3
3
  module Primer
4
4
  module Beta
5
+ # This component has been deprecated. Use [Banner](<%= link_to_component(Primer::Alpha::Banner) %>) instead.
6
+ #
5
7
  # Use `Flash` to inform users of successful or pending actions.
6
8
  class Flash < Primer::Component
7
- status :beta
9
+ status :deprecated
8
10
 
9
11
  # Optional action content showed on the right side of the component.
10
12
  #
@@ -47,21 +47,19 @@ let NavListElement = class NavListElement extends HTMLElement {
47
47
  }
48
48
  // expand collapsible item onClick
49
49
  expandItem(item) {
50
- var _a;
51
- (_a = item.nextElementSibling) === null || _a === void 0 ? void 0 : _a.removeAttribute('data-hidden');
50
+ item.nextElementSibling?.removeAttribute('data-hidden');
52
51
  item.setAttribute('aria-expanded', 'true');
53
52
  }
54
53
  collapseItem(item) {
55
- var _a;
56
- (_a = item.nextElementSibling) === null || _a === void 0 ? void 0 : _a.setAttribute('data-hidden', '');
54
+ item.nextElementSibling?.setAttribute('data-hidden', '');
57
55
  item.setAttribute('aria-expanded', 'false');
58
56
  item.focus();
59
57
  }
60
58
  itemIsExpanded(item) {
61
- if ((item === null || item === void 0 ? void 0 : item.tagName) === 'A') {
59
+ if (item?.tagName === 'A') {
62
60
  return true;
63
61
  }
64
- return (item === null || item === void 0 ? void 0 : item.getAttribute('aria-expanded')) === 'true';
62
+ return item?.getAttribute('aria-expanded') === 'true';
65
63
  }
66
64
  // expand/collapse item
67
65
  handleItemWithSubItemClick(e) {
@@ -77,6 +75,7 @@ let NavListElement = class NavListElement extends HTMLElement {
77
75
  else {
78
76
  this.expandItem(button);
79
77
  }
78
+ /* eslint-disable-next-line no-restricted-syntax */
80
79
  e.stopPropagation();
81
80
  }
82
81
  // collapse item
@@ -97,18 +96,18 @@ let NavListElement = class NavListElement extends HTMLElement {
97
96
  if (this.itemIsExpanded(button) && e.key === 'Escape') {
98
97
  this.collapseItem(button);
99
98
  }
99
+ /* eslint-disable-next-line no-restricted-syntax */
100
100
  e.stopPropagation();
101
101
  }
102
102
  };
103
103
  _NavListElement_instances = new WeakSet();
104
104
  _NavListElement_findSelectedNavItemById = function _NavListElement_findSelectedNavItemById(itemId) {
105
- var _a;
106
105
  // First we compare the selected link to data-item-id for each nav item
107
106
  for (const navItem of this.items) {
108
107
  if (navItem.classList.contains('ActionListItem--hasSubItem')) {
109
108
  continue;
110
109
  }
111
- const keys = ((_a = navItem.getAttribute('data-item-id')) === null || _a === void 0 ? void 0 : _a.split(' ')) || [];
110
+ const keys = navItem.getAttribute('data-item-id')?.split(' ') || [];
112
111
  if (keys.includes(itemId)) {
113
112
  return navItem;
114
113
  }
@@ -152,10 +151,9 @@ _NavListElement_deselect = function _NavListElement_deselect(navItem) {
152
151
  }
153
152
  };
154
153
  _NavListElement_findParentMenu = function _NavListElement_findParentMenu(navItem) {
155
- var _a;
156
154
  if (!navItem.classList.contains('ActionListItem--subItem'))
157
155
  return null;
158
- const parent = (_a = navItem.closest('li.ActionListItem--hasSubItem')) === null || _a === void 0 ? void 0 : _a.querySelector('button.ActionListContent');
156
+ const parent = navItem.closest('li.ActionListItem--hasSubItem')?.querySelector('button.ActionListContent');
159
157
  if (parent) {
160
158
  return parent;
161
159
  }
@@ -74,6 +74,7 @@ export class NavListElement extends HTMLElement {
74
74
  this.expandItem(button)
75
75
  }
76
76
 
77
+ /* eslint-disable-next-line no-restricted-syntax */
77
78
  e.stopPropagation()
78
79
  }
79
80
 
@@ -96,6 +97,7 @@ export class NavListElement extends HTMLElement {
96
97
  this.collapseItem(button)
97
98
  }
98
99
 
100
+ /* eslint-disable-next-line no-restricted-syntax */
99
101
  e.stopPropagation()
100
102
  }
101
103
 
@@ -44,7 +44,6 @@ let NavListGroupElement = class NavListGroupElement extends HTMLElement {
44
44
  return this.showMoreItem.getAttribute('src') || '';
45
45
  }
46
46
  async showMore(e) {
47
- var _a, _b;
48
47
  e.preventDefault();
49
48
  if (this.showMoreDisabled)
50
49
  return;
@@ -69,11 +68,11 @@ let NavListGroupElement = class NavListGroupElement extends HTMLElement {
69
68
  return;
70
69
  }
71
70
  const fragment = __classPrivateFieldGet(this, _NavListGroupElement_instances, "m", _NavListGroupElement_parseHTML).call(this, document, html);
72
- (_a = fragment === null || fragment === void 0 ? void 0 : fragment.querySelector('li > a')) === null || _a === void 0 ? void 0 : _a.setAttribute('data-targets', 'nav-list-group.focusMarkers');
71
+ fragment?.querySelector('li > a')?.setAttribute('data-targets', 'nav-list-group.focusMarkers');
73
72
  const listId = e.target.closest('button').getAttribute('data-list-id');
74
73
  const list = document.getElementById(listId);
75
74
  list.append(fragment);
76
- (_b = this.focusMarkers.pop()) === null || _b === void 0 ? void 0 : _b.focus();
75
+ this.focusMarkers.pop()?.focus();
77
76
  this.showMoreDisabled = false;
78
77
  }
79
78
  setShowMoreItemState() {
@@ -12,11 +12,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
12
12
  var _DialogHelperElement_abortController;
13
13
  function dialogInvokerButtonHandler(event) {
14
14
  const target = event.target;
15
- const button = target === null || target === void 0 ? void 0 : target.closest('button');
15
+ const button = target?.closest('button');
16
16
  if (!button || button.hasAttribute('disabled') || button.getAttribute('aria-disabled') === 'true')
17
17
  return;
18
18
  // If the user is clicking a valid dialog trigger
19
- let dialogId = button === null || button === void 0 ? void 0 : button.getAttribute('data-show-dialog-id');
19
+ let dialogId = button?.getAttribute('data-show-dialog-id');
20
20
  if (dialogId) {
21
21
  const dialog = document.getElementById(dialogId);
22
22
  if (dialog instanceof HTMLDialogElement) {
@@ -25,6 +25,41 @@ function dialogInvokerButtonHandler(event) {
25
25
  // If the behaviour is allowed through the dialog will be shown but then
26
26
  // quickly hidden- as if it were never shown. This prevents that.
27
27
  event.preventDefault();
28
+ // In some older browsers, such as Chrome 122, when a top layer element (such as a dialog)
29
+ // opens from within a popover, the "hide all popovers" internal algorithm runs, hiding
30
+ // any popover that is currently open, regardless of whether or not another top layer element,
31
+ // such as a <dialog> is nested inside.
32
+ // See https://github.com/whatwg/html/issues/9998.
33
+ // This is fixed by https://github.com/whatwg/html/pull/10116, but while we still support browsers
34
+ // that present this bug, we must undo the work they did to hide ancestral popovers of the dialog:
35
+ let node = button;
36
+ let fixed = false;
37
+ while (node) {
38
+ node = node.parentElement?.closest('[popover]:not(:popover-open)');
39
+ if (node && node.popover === 'auto') {
40
+ node.classList.add('dialog-inside-popover-fix');
41
+ node.popover = 'manual';
42
+ node.showPopover();
43
+ fixed = true;
44
+ }
45
+ }
46
+ if (fixed) {
47
+ // We need to re-open the dialog as modal, and also ensure no close event listeners
48
+ // are trying to act on the close
49
+ /* eslint-disable-next-line no-restricted-syntax */
50
+ dialog.addEventListener('close', e => e.stopImmediatePropagation(), { once: true });
51
+ dialog.close();
52
+ dialog.showModal();
53
+ dialog.addEventListener('close', () => {
54
+ for (const el of dialog.ownerDocument.querySelectorAll('.dialog-inside-popover-fix')) {
55
+ if (el.contains(dialog)) {
56
+ el.classList.remove('dialog-inside-popover-fix');
57
+ el.popover = 'auto';
58
+ el.showPopover();
59
+ }
60
+ }
61
+ }, { once: true });
62
+ }
28
63
  }
29
64
  }
30
65
  dialogId = button.getAttribute('data-close-dialog-id') || button.getAttribute('data-submit-dialog-id');
@@ -52,34 +87,18 @@ export class DialogHelperElement extends HTMLElement {
52
87
  for (const record of records) {
53
88
  if (record.target === this.dialog) {
54
89
  this.ownerDocument.body.classList.toggle('has-modal', this.dialog.hasAttribute('open'));
55
- // In some older browsers, such as Chrome 122, when a top layer element (such as a dialog)
56
- // opens from within a popover, the "hide all popovers" internal algorithm runs, hiding
57
- // any popover that is currently open, regardless of whether or not another top layer element,
58
- // such as a <dialog> is nested inside.
59
- // See https://github.com/whatwg/html/issues/9998.
60
- // This is fixed by https://github.com/whatwg/html/pull/10116, but while we still support browsers that present this bug,
61
- // we must undo the work they did to hide ancestral popovers of the dialog:
62
- if (this.dialog.hasAttribute('open')) {
63
- let node = this.dialog;
64
- while (node) {
65
- node = node.closest('[popover]:not(:popover-open)');
66
- if (node)
67
- node.showPopover();
68
- }
69
- }
70
90
  }
71
91
  }
72
92
  }).observe(this, { subtree: true, attributeFilter: ['open'] });
73
93
  }
74
94
  disconnectedCallback() {
75
- var _a;
76
- (_a = __classPrivateFieldGet(this, _DialogHelperElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
95
+ __classPrivateFieldGet(this, _DialogHelperElement_abortController, "f")?.abort();
77
96
  }
78
97
  handleEvent(event) {
79
98
  const target = event.target;
80
99
  const dialog = this.dialog;
81
100
  // The click target _must_ be the dialog element itself, and not elements underneath or inside.
82
- if (target !== dialog || !(dialog === null || dialog === void 0 ? void 0 : dialog.open))
101
+ if (target !== dialog || !dialog?.open)
83
102
  return;
84
103
  const rect = dialog.getBoundingClientRect();
85
104
  const clickWasInsideDialog = rect.top <= event.clientY &&
@@ -14,6 +14,46 @@ function dialogInvokerButtonHandler(event: Event) {
14
14
  // If the behaviour is allowed through the dialog will be shown but then
15
15
  // quickly hidden- as if it were never shown. This prevents that.
16
16
  event.preventDefault()
17
+
18
+ // In some older browsers, such as Chrome 122, when a top layer element (such as a dialog)
19
+ // opens from within a popover, the "hide all popovers" internal algorithm runs, hiding
20
+ // any popover that is currently open, regardless of whether or not another top layer element,
21
+ // such as a <dialog> is nested inside.
22
+ // See https://github.com/whatwg/html/issues/9998.
23
+ // This is fixed by https://github.com/whatwg/html/pull/10116, but while we still support browsers
24
+ // that present this bug, we must undo the work they did to hide ancestral popovers of the dialog:
25
+ let node: HTMLElement | null | undefined = button
26
+ let fixed = false
27
+ while (node) {
28
+ node = node.parentElement?.closest('[popover]:not(:popover-open)')
29
+ if (node && node.popover === 'auto') {
30
+ node.classList.add('dialog-inside-popover-fix')
31
+ node.popover = 'manual'
32
+ node.showPopover()
33
+ fixed = true
34
+ }
35
+ }
36
+ if (fixed) {
37
+ // We need to re-open the dialog as modal, and also ensure no close event listeners
38
+ // are trying to act on the close
39
+ /* eslint-disable-next-line no-restricted-syntax */
40
+ dialog.addEventListener('close', e => e.stopImmediatePropagation(), {once: true})
41
+ dialog.close()
42
+ dialog.showModal()
43
+ dialog.addEventListener(
44
+ 'close',
45
+ () => {
46
+ for (const el of dialog.ownerDocument.querySelectorAll<HTMLElement>('.dialog-inside-popover-fix')) {
47
+ if (el.contains(dialog)) {
48
+ el.classList.remove('dialog-inside-popover-fix')
49
+ el.popover = 'auto'
50
+ el.showPopover()
51
+ }
52
+ }
53
+ },
54
+ {once: true},
55
+ )
56
+ }
17
57
  }
18
58
  }
19
59
 
@@ -44,20 +84,6 @@ export class DialogHelperElement extends HTMLElement {
44
84
  for (const record of records) {
45
85
  if (record.target === this.dialog) {
46
86
  this.ownerDocument.body.classList.toggle('has-modal', this.dialog.hasAttribute('open'))
47
- // In some older browsers, such as Chrome 122, when a top layer element (such as a dialog)
48
- // opens from within a popover, the "hide all popovers" internal algorithm runs, hiding
49
- // any popover that is currently open, regardless of whether or not another top layer element,
50
- // such as a <dialog> is nested inside.
51
- // See https://github.com/whatwg/html/issues/9998.
52
- // This is fixed by https://github.com/whatwg/html/pull/10116, but while we still support browsers that present this bug,
53
- // we must undo the work they did to hide ancestral popovers of the dialog:
54
- if (this.dialog.hasAttribute('open')) {
55
- let node: HTMLElement | null = this.dialog
56
- while (node) {
57
- node = node.closest('[popover]:not(:popover-open)')
58
- if (node) node.showPopover()
59
- }
60
- }
61
87
  }
62
88
  }
63
89
  }).observe(this, {subtree: true, attributeFilter: ['open']})
@@ -13,7 +13,7 @@ var _FocusGroupElement_instances, _FocusGroupElement_retainSignal, _FocusGroupEl
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(', ');
16
- const getMnemonicFor = (item) => { var _a; return (_a = item.textContent) === null || _a === void 0 ? void 0 : _a.trim()[0].toLowerCase(); };
16
+ const getMnemonicFor = (item) => item.textContent?.trim()[0].toLowerCase();
17
17
  const printable = /^\S$/;
18
18
  class FocusGroupElement extends HTMLElement {
19
19
  constructor() {
@@ -54,26 +54,23 @@ class FocusGroupElement extends HTMLElement {
54
54
  this.addEventListener('focusin', this, { signal });
55
55
  }
56
56
  disconnectedCallback() {
57
- var _a;
58
- (_a = __classPrivateFieldGet(this, _FocusGroupElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
57
+ __classPrivateFieldGet(this, _FocusGroupElement_abortController, "f")?.abort();
59
58
  }
60
59
  handleEvent(event) {
61
- var _a;
62
60
  const { direction, nowrap } = this;
63
61
  if (event.type === 'focusin') {
64
62
  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();
63
+ __classPrivateFieldGet(this, _FocusGroupElement_retainSignal, "f")?.abort();
66
64
  const { signal } = (__classPrivateFieldSet(this, _FocusGroupElement_retainSignal, new AbortController(), "f"));
67
65
  for (const item of __classPrivateFieldGet(this, _FocusGroupElement_instances, "a", _FocusGroupElement_items_get)) {
68
66
  item.setAttribute('tabindex', item === event.target ? '0' : '-1');
69
67
  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) {
68
+ if (item === event.target && popover?.popover === 'auto' && popover.closest('focus-group') === this) {
71
69
  popover.addEventListener('toggle', (toggleEvent) => {
72
- var _a, _b;
73
70
  if (!(toggleEvent.target instanceof Element))
74
71
  return;
75
72
  if (toggleEvent.newState === 'closed') {
76
- (_a = __classPrivateFieldGet(this, _FocusGroupElement_retainSignal, "f")) === null || _a === void 0 ? void 0 : _a.abort();
73
+ __classPrivateFieldGet(this, _FocusGroupElement_retainSignal, "f")?.abort();
77
74
  item.setAttribute('tabindex', '-1');
78
75
  if (popover.id) {
79
76
  const invoker = this.querySelector(`[popovertarget="${popover.id}"]`);
@@ -81,7 +78,7 @@ class FocusGroupElement extends HTMLElement {
81
78
  invoker.setAttribute('tabindex', '0');
82
79
  }
83
80
  else {
84
- (_b = __classPrivateFieldGet(this, _FocusGroupElement_instances, "a", _FocusGroupElement_items_get)[0]) === null || _b === void 0 ? void 0 : _b.setAttribute('tabindex', '0');
81
+ __classPrivateFieldGet(this, _FocusGroupElement_instances, "a", _FocusGroupElement_items_get)[0]?.setAttribute('tabindex', '0');
85
82
  }
86
83
  }
87
84
  }
@@ -146,13 +143,13 @@ class FocusGroupElement extends HTMLElement {
146
143
  let el = focusEl;
147
144
  do {
148
145
  el = el.closest(`[popover]:not(:popover-open)`);
149
- if ((el === null || el === void 0 ? void 0 : el.popover) === 'auto' && !['ArrowRight', 'ArrowLeft'].includes(event.key)) {
146
+ if (el?.popover === 'auto' && !['ArrowRight', 'ArrowLeft'].includes(event.key)) {
150
147
  el.showPopover();
151
148
  }
152
- el = (el === null || el === void 0 ? void 0 : el.parentElement) || null;
149
+ el = el?.parentElement || null;
153
150
  } while (el);
154
151
  }
155
- focusEl === null || focusEl === void 0 ? void 0 : focusEl.focus();
152
+ focusEl?.focus();
156
153
  }
157
154
  }
158
155
  }
@@ -85,7 +85,7 @@ export default class FocusGroupElement extends HTMLElement {
85
85
  }
86
86
  }
87
87
  },
88
- {signal}
88
+ {signal},
89
89
  )
90
90
  }
91
91
  }
@@ -33,6 +33,11 @@ deprecations:
33
33
  autocorrect: true
34
34
  replacement: "Primer::Beta::AutoComplete::Item"
35
35
 
36
+ - component: "Primer::Beta::Flash"
37
+ autocorrect: false
38
+ replacement: "Primer::Alpha::Banner"
39
+ guide: "https://primer.style/components/banner/rails/alpha"
40
+
36
41
  - component: "Primer::BlankslateComponent"
37
42
  autocorrect: true
38
43
  replacement: "Primer::Beta::Blankslate"
@@ -8,7 +8,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
8
8
  import { controller, targets } from '@github/catalyst';
9
9
  let PrimerMultiInputElement = class PrimerMultiInputElement extends HTMLElement {
10
10
  activateField(name) {
11
- var _a, _b;
12
11
  const fieldWithName = this.findField(name);
13
12
  if (!fieldWithName)
14
13
  return;
@@ -17,11 +16,11 @@ let PrimerMultiInputElement = class PrimerMultiInputElement extends HTMLElement
17
16
  continue;
18
17
  field.setAttribute('disabled', 'disabled');
19
18
  field.setAttribute('hidden', 'hidden');
20
- (_a = field.parentElement) === null || _a === void 0 ? void 0 : _a.setAttribute('hidden', 'hidden');
19
+ field.parentElement?.setAttribute('hidden', 'hidden');
21
20
  }
22
21
  fieldWithName.removeAttribute('disabled');
23
22
  fieldWithName.removeAttribute('hidden');
24
- (_b = fieldWithName.parentElement) === null || _b === void 0 ? void 0 : _b.removeAttribute('hidden');
23
+ fieldWithName.parentElement?.removeAttribute('hidden');
25
24
  }
26
25
  findField(name) {
27
26
  for (const field of this.fields) {
@@ -27,8 +27,7 @@ class PrimerTextFieldElement extends HTMLElement {
27
27
  _PrimerTextFieldElement_abortController.set(this, void 0);
28
28
  }
29
29
  connectedCallback() {
30
- var _a;
31
- (_a = __classPrivateFieldGet(this, _PrimerTextFieldElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
30
+ __classPrivateFieldGet(this, _PrimerTextFieldElement_abortController, "f")?.abort();
32
31
  const { signal } = (__classPrivateFieldSet(this, _PrimerTextFieldElement_abortController, new AbortController(), "f"));
33
32
  this.inputElement.addEventListener('auto-check-success', async (event) => {
34
33
  const message = await event.detail.response.text();
@@ -45,8 +44,7 @@ class PrimerTextFieldElement extends HTMLElement {
45
44
  }, { signal });
46
45
  }
47
46
  disconnectedCallback() {
48
- var _a;
49
- (_a = __classPrivateFieldGet(this, _PrimerTextFieldElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
47
+ __classPrivateFieldGet(this, _PrimerTextFieldElement_abortController, "f")?.abort();
50
48
  }
51
49
  clearContents() {
52
50
  this.inputElement.value = '';
@@ -5,8 +5,8 @@ module Primer
5
5
  module ViewComponents
6
6
  module VERSION
7
7
  MAJOR = 0
8
- MINOR = 18
9
- PATCH = 1
8
+ MINOR = 19
9
+ PATCH = 0
10
10
 
11
11
  STRING = [MAJOR, MINOR, PATCH].join(".")
12
12
  end
@@ -1,9 +1,20 @@
1
- <%= render(Primer::Alpha::Overlay.new(title: "An overlay")) do |o| %>
1
+ <%= render(Primer::Alpha::Overlay.new(title: "An overlay", id: "first-overlay")) do |o| %>
2
2
  <% o.with_show_button() { "Show overlay" } %>
3
3
  <% o.with_body() do %>
4
4
  <%= render(Primer::Alpha::Dialog.new(id: "dialog-one", title: title, position: position, subtitle: subtitle, visually_hide_title: false)) do |d| %>
5
5
  <% d.with_show_button { button_text } %>
6
- <% d.with_body { body_text} %>
6
+ <% d.with_body { body_text } %>
7
+ <% d.with_footer(show_divider: true) do %>
8
+ <%= render(Primer::ButtonComponent.new(data: { "close-dialog-id": "dialog-one" })) { "Cancel" } %>
9
+ <% end %>
7
10
  <% end %>
8
11
  <% end %>
9
12
  <% end %>
13
+
14
+ <script type="module">
15
+ document.getElementById('overlay-show-first-overlay')?.addEventListener('click', e => {
16
+ setTimeout(() => {
17
+ document.getElementById('first-overlay').querySelector('button')?.click()
18
+ });
19
+ });
20
+ </script>
@@ -259,6 +259,7 @@ module Primer
259
259
  # @param visually_hide_title [Boolean] toggle
260
260
  # @param button_text [String] text
261
261
  # @param body_text [String] text
262
+ # @snapshot interactive
262
263
  def dialog_inside_overlay(title: "Test Dialog", subtitle: nil, position: :center, size: :medium, button_text: "Show Dialog", body_text: "Content", position_narrow: :fullscreen, visually_hide_title: false)
263
264
  render_with_template(locals: {
264
265
  title: title,
@@ -0,0 +1,7 @@
1
+ <%= render(Primer::Beta::ButtonGroup.new) do |component| %>
2
+ <% component.with_button { "Main menu" } %>
3
+ <% component.with_menu_button(button_arguments: { "aria-label": "secondary menu" }, menu_arguments: { anchor_align: :end }) do |menu, button| %>
4
+ <% menu.with_item(label: "Item 1", item_id: :item1) %>
5
+ <% menu.with_item(label: "Item 2", item_id: :item2) %>
6
+ <% end %>
7
+ <% end %>