primer_view_components 0.0.104 → 0.0.106

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -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_list/item.rb +2 -2
  8. data/app/components/primer/alpha/action_list.rb +2 -0
  9. data/app/components/primer/alpha/image.rb +50 -0
  10. data/app/components/primer/{image_crop.d.ts → alpha/image_crop.d.ts} +0 -0
  11. data/app/components/primer/{image_crop.html.erb → alpha/image_crop.html.erb} +0 -0
  12. data/app/components/primer/{image_crop.js → alpha/image_crop.js} +0 -0
  13. data/app/components/primer/alpha/image_crop.rb +39 -0
  14. data/app/components/primer/{image_crop.ts → alpha/image_crop.ts} +0 -0
  15. data/app/components/primer/alpha/nav_list/item.rb +2 -0
  16. data/app/components/primer/alpha/nav_list/section.rb +2 -0
  17. data/app/components/primer/alpha/segmented_control/item.html.erb +13 -0
  18. data/app/components/primer/alpha/segmented_control/item.rb +29 -0
  19. data/app/components/primer/alpha/segmented_control.css +1 -0
  20. data/app/components/primer/alpha/segmented_control.css.map +1 -0
  21. data/app/components/primer/alpha/segmented_control.d.ts +11 -0
  22. data/app/components/primer/alpha/segmented_control.html.erb +7 -0
  23. data/app/components/primer/alpha/segmented_control.js +48 -0
  24. data/app/components/primer/alpha/segmented_control.pcss +121 -0
  25. data/app/components/primer/alpha/segmented_control.rb +92 -0
  26. data/app/components/primer/alpha/segmented_control.ts +41 -0
  27. data/app/components/primer/alpha/tooltip.rb +2 -2
  28. data/app/components/primer/beta/auto_complete/item.rb +2 -2
  29. data/app/components/primer/beta/avatar.rb +1 -1
  30. data/app/components/primer/beta/banner.css +1 -0
  31. data/app/components/primer/beta/banner.css.map +1 -0
  32. data/app/components/primer/beta/banner.html.erb +33 -0
  33. data/app/components/primer/beta/banner.pcss +126 -0
  34. data/app/components/primer/beta/banner.rb +128 -0
  35. data/app/components/primer/beta/blankslate.rb +4 -4
  36. data/app/components/primer/beta/breadcrumbs.rb +1 -1
  37. data/app/components/primer/beta/button.rb +1 -1
  38. data/app/components/primer/beta/label.rb +100 -0
  39. data/app/components/primer/beta/link.rb +99 -0
  40. data/app/components/primer/beta/x_banner.d.ts +11 -0
  41. data/app/components/primer/beta/x_banner.js +39 -0
  42. data/app/components/primer/beta/x_banner.ts +39 -0
  43. data/app/components/primer/component.rb +1 -0
  44. data/app/components/primer/image.rb +2 -41
  45. data/app/components/primer/image_crop.rb +2 -32
  46. data/app/components/primer/label_component.rb +2 -93
  47. data/app/components/primer/link_component.rb +2 -92
  48. data/app/components/primer/primer.d.ts +3 -1
  49. data/app/components/primer/primer.js +3 -1
  50. data/app/components/primer/primer.pcss +2 -0
  51. data/app/components/primer/primer.ts +3 -1
  52. data/app/components/primer/subhead_component.rb +1 -1
  53. data/app/components/primer/time_ago_component.rb +2 -0
  54. data/app/helpers/primer/form_helper.rb +1 -0
  55. data/app/lib/primer/view_helper.rb +1 -1
  56. data/lib/primer/deprecations.rb +4 -0
  57. data/lib/primer/form_components.rb +1 -0
  58. data/lib/primer/view_components/linters/argument_mappers/flash.rb +5 -5
  59. data/lib/primer/view_components/linters/argument_mappers/label.rb +4 -4
  60. data/lib/primer/view_components/linters/label_component_migration_counter.rb +3 -3
  61. data/lib/primer/view_components/linters/migrate_deprecated_flash_arguments.rb +140 -0
  62. data/lib/primer/view_components/version.rb +1 -1
  63. data/lib/rubocop/config/default.yml +0 -3
  64. data/lib/rubocop/cop/primer/deprecated_label_schemes.rb +3 -3
  65. data/lib/rubocop/cop/primer/deprecated_label_variants.rb +5 -5
  66. data/lib/tasks/docs.rake +14 -7
  67. data/lib/tasks/test.rake +23 -26
  68. data/previews/primer/alpha/action_list_preview.rb +29 -0
  69. data/previews/primer/alpha/auto_complete_preview.rb +10 -0
  70. data/previews/primer/alpha/button_marketing_preview.rb +9 -0
  71. data/previews/primer/alpha/dialog_preview.rb +18 -1
  72. data/previews/primer/alpha/hidden_text_expander_preview.rb +7 -0
  73. data/previews/primer/alpha/image_crop_preview.rb +31 -0
  74. data/previews/primer/alpha/layout_preview.rb +15 -0
  75. data/previews/primer/alpha/nav_list_preview.rb +29 -0
  76. data/previews/primer/alpha/segmented_control_preview.rb +75 -0
  77. data/previews/primer/alpha/tab_panels_preview.rb +15 -0
  78. data/previews/primer/alpha/text_field_preview.rb +58 -0
  79. data/previews/primer/alpha/toggle_switch_preview.rb +4 -0
  80. data/previews/primer/alpha/tooltip_preview.rb +13 -3
  81. data/previews/primer/alpha/underline_nav_preview/playground.html.erb +8 -0
  82. data/previews/primer/alpha/underline_nav_preview.rb +15 -0
  83. data/previews/primer/alpha/underline_panels_preview.rb +15 -0
  84. data/previews/primer/beta/auto_complete_item_preview/playground.html.erb +9 -0
  85. data/previews/primer/beta/auto_complete_item_preview.rb +14 -0
  86. data/previews/primer/beta/auto_complete_preview.rb +20 -0
  87. data/previews/primer/beta/avatar_preview.rb +9 -0
  88. data/previews/primer/beta/avatar_stack_preview.rb +15 -0
  89. data/previews/primer/beta/banner_preview/with_action_button.html.erb +4 -0
  90. data/previews/primer/beta/banner_preview/with_action_content.html.erb +6 -0
  91. data/previews/primer/beta/banner_preview.rb +54 -0
  92. data/previews/primer/beta/base_button_preview.rb +9 -0
  93. data/previews/primer/beta/blankslate_preview.rb +12 -0
  94. data/previews/primer/beta/border_box_preview.rb +14 -0
  95. data/previews/primer/beta/breadcrumbs_preview.rb +11 -0
  96. data/previews/primer/beta/button_group_preview.rb +12 -0
  97. data/previews/primer/beta/close_button_preview.rb +7 -0
  98. data/previews/primer/beta/counter_preview.rb +11 -0
  99. data/previews/primer/beta/details_preview.rb +15 -0
  100. data/previews/primer/beta/flash_preview.rb +12 -0
  101. data/previews/primer/beta/heading_preview.rb +8 -0
  102. data/previews/primer/beta/label_preview.rb +26 -0
  103. data/previews/primer/beta/link_preview.rb +41 -0
  104. data/previews/primer/beta/text_preview.rb +8 -0
  105. data/previews/primer/beta/truncate_preview.rb +7 -0
  106. data/previews/primer/clipboard_copy_preview.rb +8 -0
  107. data/previews/primer/dropdown_preview.rb +18 -0
  108. data/previews/primer/hellip_button_preview.rb +8 -0
  109. data/previews/primer/layout_component_preview.rb +13 -1
  110. data/previews/primer/local_time_component_preview.rb +13 -0
  111. data/previews/primer/markdown_preview.rb +277 -266
  112. data/previews/primer/menu_component_preview/playground.html.erb +17 -0
  113. data/previews/primer/menu_component_preview.rb +4 -1
  114. data/previews/primer/octicon_component_preview.rb +8 -0
  115. data/previews/primer/popover_component_preview.rb +15 -1
  116. data/previews/primer/progress_bar_component_preview.rb +12 -1
  117. data/previews/primer/spinner_component_preview.rb +7 -0
  118. data/previews/primer/state_component_preview.rb +11 -1
  119. data/previews/primer/subhead_component_preview.rb +17 -0
  120. data/previews/primer/time_ago_component_preview.rb +8 -0
  121. data/previews/primer/timeline_item_component_preview.rb +12 -1
  122. data/static/arguments.json +356 -138
  123. data/static/audited_at.json +7 -0
  124. data/static/constants.json +101 -71
  125. data/static/statuses.json +11 -4
  126. metadata +39 -9
  127. data/previews/primer/image_crop_preview.rb +0 -22
  128. data/previews/primer/label_component_preview.rb +0 -15
  129. data/previews/primer/link_component_preview.rb +0 -29
@@ -0,0 +1,121 @@
1
+ /* SegmentedControl */
2
+
3
+ .SegmentedControl {
4
+ display: inline-flex;
5
+ list-style: none;
6
+ background-color: var(--color-segmented-control-bg);
7
+ border-radius: var(--primer-borderRadius-medium, 6px);
8
+ box-shadow: var(--primer-borderInset-thin, inset 0 0 0 max(1px, 0.0625rem)) var(--color-border-default);
9
+ }
10
+
11
+ .SegmentedControl-item {
12
+ position: relative;
13
+ display: inline-flex;
14
+ border: var(--primer-borderWidth-thin, max(1px, 0.0625rem)) solid transparent;
15
+ border-radius: var(--primer-borderRadius-medium, 6px);
16
+ padding: var(--primer-control-xsmall-paddingInline-condensed, 4px);
17
+
18
+ /* Selected ---------------------------------------- */
19
+ &.SegmentedControl-item--selected {
20
+ background-color: var(--color-btn-bg);
21
+ border-color: var(--color-segmented-control-button-selected-border);
22
+
23
+ & .Button {
24
+ font-weight: var(--base-text-weight-semibold, 600);
25
+
26
+ &:hover {
27
+ background-color: transparent;
28
+ }
29
+ }
30
+
31
+ &::before {
32
+ border-color: transparent;
33
+ }
34
+
35
+ & + .SegmentedControl-item::before {
36
+ border-color: transparent;
37
+ }
38
+ }
39
+
40
+ /* renders a visibly hidden "copy" of the text in bold, reserving box space for when text becomes bold on selected */
41
+ & .Button-label[data-content]::before {
42
+ display: block;
43
+ height: 0;
44
+ font-weight: var(--base-text-weight-semibold, 600);
45
+ visibility: hidden;
46
+ content: attr(data-content);
47
+ }
48
+
49
+ /* Separator lines */
50
+ &::before {
51
+ position: absolute;
52
+ inset: 0 0 0 -1px;
53
+ margin-top: var(--primer-control-medium-paddingBlock, 6px);
54
+ margin-bottom: var(--primer-control-medium-paddingBlock, 6px);
55
+ content: '';
56
+ border-left: var(--primer-borderWidth-thin, max(1px, 0.0625rem)) solid var(--color-border-default);
57
+ }
58
+
59
+ /* Button ----------------------------------------- */
60
+ & .Button {
61
+ border: 0;
62
+ font-weight: var(--base-text-weight-normal, 400);
63
+ transition: none;
64
+ color: var(--color-btn-text);
65
+
66
+ &:focus-visible {
67
+ outline-offset: calc(var(--primer-control-xsmall-paddingInline-condensed, 4px) - var(--primer-borderWidth-thin, 1px));
68
+ border-radius: calc(var(--primer-borderRadius-medium, 6px) - 5px);
69
+ }
70
+ }
71
+
72
+ & .Button--small {
73
+ height: calc(var(--primer-control-small-size, 28px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
74
+ padding: 0 calc(var(--primer-control-small-paddingInline-condensed, 8px) - var(--primer-control-xsmall-paddingInline-condensed, 4px));
75
+
76
+ &.Button--iconOnly {
77
+ width: calc(var(--primer-control-medium-size, 32px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
78
+ }
79
+ }
80
+
81
+ & .Button--medium {
82
+ height: calc(var(--primer-control-medium-size, 32px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
83
+ padding: 0 calc(var(--primer-control-medium-paddingInline-normal, 12px) - var(--primer-control-xsmall-paddingInline-condensed, 4px));
84
+
85
+ &.Button--iconOnly {
86
+ width: calc(var(--primer-control-medium-size, 32px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
87
+ }
88
+ }
89
+
90
+ & .Button--large {
91
+ height: calc(var(--primer-control-large-size, 40px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
92
+ padding: 0 calc(var(--primer-control-large-paddingInline-spacious, 16px) - var(--primer-control-xsmall-paddingInline-condensed, 4px));
93
+
94
+ &.Button--iconOnly {
95
+ width: calc(var(--primer-control-large-size, 40px) - var(--primer-control-xsmall-paddingInline-condensed, 4px) * 2 - var(--primer-borderWidth-thin, 1px) * 2);
96
+ }
97
+ }
98
+
99
+ & .Button--iconOnly {
100
+ padding: initial;
101
+ }
102
+
103
+ & .Button--invisible.Button--invisible-noVisuals .Button-label {
104
+ color: var(--color-btn-text);
105
+ }
106
+ }
107
+
108
+ /* fullWidth */
109
+ .SegmentedControl--fullWidth {
110
+ display: flex;
111
+
112
+ & .SegmentedControl-item {
113
+ flex: 1;
114
+ justify-content: center;
115
+ }
116
+
117
+ & .Button--iconOnly,
118
+ & .Button-withTooltip {
119
+ width: 100%;
120
+ }
121
+ }
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Alpha
5
+ # Use a segmented control to let users select an option from a short list and immediately apply the selection
6
+ class SegmentedControl < Primer::Component
7
+ status :alpha
8
+
9
+ FULL_WIDTH_DEFAULT = false
10
+ HIDE_LABELS_DEFAULT = false
11
+
12
+ # Use to render an item in the segmented control
13
+ #
14
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
15
+ renders_many :items, lambda { |label:, selected: false, icon: nil, **system_arguments|
16
+ Primer::Alpha::SegmentedControl::Item.new(
17
+ label: label,
18
+ selected: selected,
19
+ icon: icon,
20
+ hide_labels: @hide_labels,
21
+ size: @size,
22
+ block: @full_width,
23
+ **system_arguments
24
+ )
25
+ }
26
+
27
+ # @example Basic usage
28
+ #
29
+ # <%= render(Primer::Alpha::SegmentedControl.new) do |c| %>
30
+ # <%= c.with_item(label: "Preview", selected: true) %>
31
+ # <%= c.with_item(label: "Raw") %>
32
+ # <%= c.with_item(label: "Blame") %>
33
+ # <% end %>
34
+ #
35
+ # @example Small
36
+ #
37
+ # <%= render(Primer::Alpha::SegmentedControl.new(size: :small)) do |c| %>
38
+ # <%= c.with_item(label: "Preview", selected: true) %>
39
+ # <%= c.with_item(label: "Raw") %>
40
+ # <%= c.with_item(label: "Blame") %>
41
+ # <% end %>
42
+ #
43
+ # @example With icons
44
+ # <%= render(Primer::Alpha::SegmentedControl.new) do |c| %>
45
+ # <%= c.with_item(label: "Preview", icon: :eye, selected: true) %>
46
+ # <%= c.with_item(label: "Raw", icon: :"file-code") %>
47
+ # <%= c.with_item(label: "Blame", icon: :people) %>
48
+ # <% end %>
49
+ #
50
+ # @example With icons only
51
+ # <%= render(Primer::Alpha::SegmentedControl.new(hide_labels: true)) do |c| %>
52
+ # <%= c.with_item(label: "Preview", icon: :eye, selected: true) %>
53
+ # <%= c.with_item(label: "Raw", icon: :"file-code") %>
54
+ # <%= c.with_item(label: "Blame", icon: :people) %>
55
+ # <% end %>
56
+ #
57
+ # @example Fill width of parent
58
+ # <%= render(Primer::Alpha::SegmentedControl.new(full_width: true)) do |c| %>
59
+ # <%= c.with_item(label: "Preview", icon: :eye, selected: true) %>
60
+ # <%= c.with_item(label: "Raw", icon: :"file-code") %>
61
+ # <%= c.with_item(label: "Blame", icon: :people) %>
62
+ # <% end %>
63
+ #
64
+ # @param hide_labels [Boolean] Whether to hide the labels and only show the icons
65
+ # @param full_width [Boolean] If the component should be full width
66
+ # @param size [Symbol] <%= one_of(Primer::Beta::Button::SIZE_OPTIONS) %>
67
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
68
+ def initialize(hide_labels: HIDE_LABELS_DEFAULT, full_width: FULL_WIDTH_DEFAULT, size: Primer::Beta::Button::DEFAULT_SIZE, **system_arguments)
69
+ @full_width = full_width
70
+ @size = size
71
+ @hide_labels = hide_labels
72
+
73
+ @system_arguments = system_arguments
74
+ @system_arguments[:tag] = :ul
75
+ @system_arguments[:role] = "list"
76
+ @system_arguments[:classes] = class_names(
77
+ system_arguments[:classes],
78
+ "SegmentedControl",
79
+ "SegmentedControl--iconOnly": hide_labels,
80
+ "SegmentedControl--fullWidth": full_width
81
+ )
82
+ end
83
+
84
+ def render?
85
+ valid_items_count = items.count <= (@hide_labels ? 6 : 5) && items.count >= 2
86
+ raise ArgumentError, "A segmented control should have 2–5 choices with text labels, or up to 6 icon-only buttons." if !valid_items_count && !Rails.env.production?
87
+
88
+ valid_items_count
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,41 @@
1
+ import {controller, targets} from '@github/catalyst'
2
+
3
+ @controller
4
+ export class SegmentedControlElement extends HTMLElement {
5
+ @targets items: HTMLElement[]
6
+
7
+ connectedCallback() {
8
+ this.#updateButtonLabels()
9
+ }
10
+
11
+ select(event: Event) {
12
+ const button = event.currentTarget as HTMLButtonElement
13
+ for (const item of this.items) {
14
+ item.classList.remove('SegmentedControl-item--selected')
15
+ item.querySelector('[aria-current]')?.setAttribute('aria-current', 'false')
16
+ }
17
+
18
+ button.closest('li.SegmentedControl-item')?.classList.add('SegmentedControl-item--selected')
19
+ button.setAttribute('aria-current', 'true')
20
+ }
21
+
22
+ // Updates the button labels to have a data-content attribute with the text
23
+ // This is for selection styling to avoid the text jumping. It only needs to be
24
+ // setup when the component is first loaded.
25
+ #updateButtonLabels() {
26
+ for (const label of this.querySelectorAll('.Button-label')) {
27
+ label.setAttribute('data-content', label.textContent || '')
28
+ }
29
+ }
30
+ }
31
+
32
+ declare global {
33
+ interface Window {
34
+ SegmentedControlElement: typeof SegmentedControlElement
35
+ }
36
+ }
37
+
38
+ if (!window.customElements.get('segmented-control')) {
39
+ window.SegmentedControlElement = SegmentedControlElement
40
+ window.customElements.define('segmented-control', SegmentedControlElement)
41
+ }
@@ -7,12 +7,12 @@ module Primer
7
7
  #
8
8
  # When using a tooltip, follow the provided guidelines to avoid accessibility issues:
9
9
  # - Tooltips should contain only **non-essential text**. Tooltips can easily be missed and are not accessible on touch devices so never use tooltips to convey critical information.
10
- # - `Tooltip` should be rendered through the API of <%= link_to_component(Primer::ButtonComponent)%>, <%= link_to_component(Primer::LinkComponent)%>, or <%= link_to_component(Primer::IconButton)%>. Avoid using `Tooltip` a standalone component unless absolutely necessary (and **only** on interactive elements).
10
+ # - `Tooltip` should be rendered through the API of <%= link_to_component(Primer::ButtonComponent)%>, <%= link_to_component(Primer::Beta::Link)%>, or <%= link_to_component(Primer::IconButton)%>. Avoid using `Tooltip` a standalone component unless absolutely necessary (and **only** on interactive elements).
11
11
  # @accessibility
12
12
  # - Tooltip text must be brief and concise whether it is a label or a description.
13
13
  # - Tooltip can only hold string content.
14
14
  # - **Never set tooltips on static, non-interactive elements** like `span` or `div`. Tooltips should only be used on interactive elements like buttons or links to avoid excluding keyboard-only users
15
- # and screen reader users. Use of tooltip through <%= link_to_component(Primer::ButtonComponent) %>, <%= link_to_component(Primer::LinkComponent) %>, or <%= link_to_component(Primer::IconButton) %> will guarantee this.
15
+ # and screen reader users. Use of tooltip through <%= link_to_component(Primer::ButtonComponent) %>, <%= link_to_component(Primer::Beta::Link) %>, or <%= link_to_component(Primer::IconButton) %> will guarantee this.
16
16
  # - If you must use `Tooltip` as a standalone component, place it adjacent after the trigger element in the DOM. This allows screen reader users to navigate to and copy the tooltip
17
17
  # content.
18
18
  # ### Which type should I set?
@@ -22,10 +22,10 @@ module Primer
22
22
 
23
23
  # The trailing visual rendered after the link.
24
24
  #
25
- # @param kwargs [Hash] The arguments accepted by <%= link_to_component(Primer::OcticonComponent) %>, <%= link_to_component(Primer::LabelComponent) %>, or <%= link_to_component(Primer::Beta::Counter) %>
25
+ # @param kwargs [Hash] The arguments accepted by <%= link_to_component(Primer::OcticonComponent) %>, <%= link_to_component(Primer::Beta::Label) %>, or <%= link_to_component(Primer::Beta::Counter) %>
26
26
  renders_one :trailing_visual, types: {
27
27
  icon: Primer::OcticonComponent,
28
- label: Primer::LabelComponent,
28
+ label: Primer::Beta::Label,
29
29
  counter: Primer::Beta::Counter
30
30
  }
31
31
 
@@ -72,7 +72,7 @@ module Primer
72
72
 
73
73
  def call
74
74
  if @href
75
- render(Primer::LinkComponent.new(href: @href, classes: @system_arguments[:classes])) do
75
+ render(Primer::Beta::Link.new(href: @href, classes: @system_arguments[:classes])) do
76
76
  render(Primer::BaseComponent.new(**@system_arguments.except(:classes))) { content }
77
77
  end
78
78
  else
@@ -0,0 +1 @@
1
+ .Banner{background-image:linear-gradient(var(--color-accent-subtle),var(--color-accent-subtle));border:var(--primer-borderWidth-thin,max(1px,.0625rem)) solid var(--color-accent-muted);border-radius:var(--primer-borderRadius-medium,6px);color:var(--color-fg-default);display:grid;grid-auto-flow:column;grid-template-areas:"visual message actions close";grid-template-columns:min-content 1fr minmax(0,auto) min-content;grid-template-rows:min-content;padding:var(--base-size-8,8px);position:relative}@media (max-width:543.98px){.Banner{grid-template-areas:"visual message close" ". actions actions";grid-template-columns:min-content 1fr min-content;grid-template-rows:min-content min-content}.Banner .Banner-actions{margin:var(--base-size-8,8px) 0 0 var(--base-size-8,8px)}}.Banner .Banner-visual{align-self:start;display:grid;grid-area:visual;padding:var(--base-size-6,6px) var(--base-size-8,8px)}.Banner .Banner-visual>.octicon{margin-bottom:calc(var(--base-size-4, 4px)/2);margin-top:calc(var(--base-size-4, 4px)/2)}.Banner .Banner-visual>*{align-self:center}.Banner .Banner-message{align-self:center;grid-area:message;padding:var(--base-size-6,6px) var(--base-size-8,8px)}.Banner .Banner-message p:last-child{margin-bottom:0}.Banner .Banner-message .Banner-title:not(:only-child){font-weight:var(--base-text-weight-semibold,600);margin-bottom:0}.Banner .Banner-actions{grid-area:actions}.Banner .Banner-actions:last-child{align-self:center}.Banner .Banner-close{grid-area:close;margin-left:var(--primer-controlStack-medium-gap-condensed,8px)}.Banner .Banner-visual .octicon{color:var(--color-accent-fg)}.Banner.Banner--warning{background-image:linear-gradient(var(--color-attention-subtle),var(--color-attention-subtle));border-color:var(--color-attention-muted);color:var(--color-fg-default)}.Banner.Banner--warning .Banner-visual .octicon{color:var(--color-attention-fg)}.Banner.Banner--error{background-image:linear-gradient(var(--color-danger-subtle),var(--color-danger-subtle));border-color:var(--color-danger-muted);color:var(--color-fg-default)}.Banner.Banner--error .Banner-visual .octicon{color:var(--color-danger-fg)}.Banner.Banner--success{background-image:linear-gradient(var(--color-success-subtle),var(--color-success-subtle));border-color:var(--color-success-muted);color:var(--color-fg-default)}.Banner.Banner--success .Banner-visual .octicon{color:var(--color-success-fg)}.Banner.Banner--full{border-left:0;border-radius:0;border-right:0;margin-top:calc(var(--primer-borderWidth-thin, max(1px, .0625rem))*-1)}@media (max-width:767.98px){.Banner.Banner--full-whenNarrow{border-left:0;border-radius:0;border-right:0;margin-top:calc(var(--primer-borderWidth-thin, max(1px, .0625rem))*-1)}}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["banner.pcss"],"names":[],"mappings":"AAEA,QAKE,uFAAyF,CACzF,uFAA2F,CAC3F,mDAAqD,CAHrD,6BAA8B,CAF9B,YAAa,CAMb,qBAAsB,CACtB,kDAAmD,CACnD,gEAAiE,CACjE,8BAA+B,CAR/B,8BAAgC,CAFhC,iBA0HF,CA7GE,4BAdF,QAeI,8DAEqB,CACrB,iDAAkD,CAClD,0CAwGJ,CAtGI,wBACI,wDACJ,CACF,CAIA,uBAIE,gBAAiB,CAHjB,YAAa,CAEb,gBAAiB,CADjB,qDAWF,CAPE,gCACE,6CAA+C,CAA/C,0CACF,CAEA,yBACE,iBACF,CAGF,wBAGE,iBAAkB,CADlB,iBAAkB,CADlB,qDAYF,CARE,qCACE,eACF,CAEA,uDAEE,gDAAkD,CADlD,eAEF,CAGF,wBACE,iBAKF,CAHE,mCACE,iBACF,CAGF,sBACE,eAAgB,CAChB,+DACF,CAEA,gCACE,4BACF,CAEA,wBAEE,6FAA+F,CAC/F,yCAA0C,CAF1C,6BAOF,CAHE,gDACE,+BACF,CAGF,sBAEE,uFAAyF,CACzF,sCAAuC,CAFvC,6BAOF,CAHE,8CACE,4BACF,CAGF,wBAEE,yFAA2F,CAC3F,uCAAwC,CAFxC,6BAOF,CAHE,gDACE,6BACF,CAKF,qBAGE,aAAc,CACd,eAAgB,CAFhB,cAAe,CADf,sEAIF,CAGA,4BACE,gCAGE,aAAc,CACd,eAAgB,CAFhB,cAAe,CADf,sEAIF,CACF","file":"banner.css","sourcesContent":["/* Banner alert */\n\n.Banner {\n position: relative;\n display: grid;\n padding: var(--base-size-8, 8px);\n color: var(--color-fg-default);\n background-image: linear-gradient(var(--color-accent-subtle), var(--color-accent-subtle));\n border: var(--primer-borderWidth-thin, max(1px, 0.0625rem)) solid var(--color-accent-muted);\n border-radius: var(--primer-borderRadius-medium, 6px);\n grid-auto-flow: column;\n grid-template-areas: 'visual message actions close';\n grid-template-columns: min-content 1fr minmax(0,auto) min-content;\n grid-template-rows: min-content;\n\n /* `sm` breakpoint variantion */\n @media (max-width: 543.98px) {\n grid-template-areas:\n 'visual message close'\n '. actions actions';\n grid-template-columns: min-content 1fr min-content;\n grid-template-rows: min-content min-content;\n\n & .Banner-actions {\n margin: var(--base-size-8, 8px) 0 0 var(--base-size-8, 8px);\n }\n }\n\n /* Elements */\n\n & .Banner-visual {\n display: grid;\n padding: var(--base-size-6, 6px) var(--base-size-8, 8px);\n grid-area: visual;\n align-self: start;\n\n & > .octicon {\n margin-block: calc(var(--base-size-4, 4px) / 2);\n }\n\n & > * {\n align-self: center;\n }\n }\n\n & .Banner-message {\n padding: var(--base-size-6, 6px) var(--base-size-8, 8px);\n grid-area: message;\n align-self: center;\n\n & p:last-child {\n margin-bottom: 0;\n }\n\n & .Banner-title:not(:only-child) {\n margin-bottom: 0;\n font-weight: var(--base-text-weight-semibold, 600);\n }\n }\n\n & .Banner-actions {\n grid-area: actions;\n\n &:last-child {\n align-self: center;\n }\n }\n\n & .Banner-close {\n grid-area: close;\n margin-left: var(--primer-controlStack-medium-gap-condensed, 8px);\n }\n\n & .Banner-visual .octicon {\n color: var(--color-accent-fg);\n }\n\n &.Banner--warning {\n color: var(--color-fg-default);\n background-image: linear-gradient(var(--color-attention-subtle), var(--color-attention-subtle));\n border-color: var(--color-attention-muted);\n\n & .Banner-visual .octicon {\n color: var(--color-attention-fg);\n }\n }\n\n &.Banner--error {\n color: var(--color-fg-default);\n background-image: linear-gradient(var(--color-danger-subtle), var(--color-danger-subtle));\n border-color: var(--color-danger-muted);\n\n & .Banner-visual .octicon {\n color: var(--color-danger-fg);\n }\n }\n\n &.Banner--success {\n color: var(--color-fg-default);\n background-image: linear-gradient(var(--color-success-subtle), var(--color-success-subtle));\n border-color: var(--color-success-muted);\n\n & .Banner-visual .octicon {\n color: var(--color-success-fg);\n }\n }\n\n /* Full-width */\n\n &.Banner--full {\n margin-top: calc(var(--primer-borderWidth-thin, max(1px, 0.0625rem)) * -1);\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n }\n\n /* --primer-viewportRange-narrowLandscape */\n @media (max-width: 767.98px) {\n &.Banner--full-whenNarrow {\n margin-top: calc(var(--primer-borderWidth-thin, max(1px, 0.0625rem)) * -1);\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n }\n }\n}\n"]}
@@ -0,0 +1,33 @@
1
+ <%= render(Primer::BaseComponent.new(**@wrapper_arguments)) do %>
2
+ <%= render(Primer::BaseComponent.new(**@system_arguments)) do %>
3
+ <% if @icon %>
4
+ <div class="Banner-visual">
5
+ <%= render(Primer::OcticonComponent.new(icon: @icon)) %>
6
+ </div>
7
+ <% end %>
8
+ <%= render(Primer::BaseComponent.new(**@message_arguments)) do %>
9
+ <p class="Banner-title" data-target="<%= catalyst_target(field: "titleText") %>"><%= content %></p>
10
+ <% if @description %>
11
+ <p><%= @description %></p>
12
+ <% end %>
13
+ <% end %>
14
+ <% if action %>
15
+ <div class="Banner-actions">
16
+ <%= action %>
17
+ </div>
18
+ <% end %>
19
+ <% if @dismissible %>
20
+ <div class="Banner-close">
21
+ <%= render(
22
+ Primer::Beta::IconButton.new(
23
+ scheme: :invisible,
24
+ icon: :x,
25
+ aria: { label: "Dismiss" },
26
+ data: { action: catalyst_action(event: "click", function: "dismiss") },
27
+ autofocus: true
28
+ )
29
+ ) %>
30
+ </div>
31
+ <% end %>
32
+ <% end %>
33
+ <% end %>
@@ -0,0 +1,126 @@
1
+ /* Banner alert */
2
+
3
+ .Banner {
4
+ position: relative;
5
+ display: grid;
6
+ padding: var(--base-size-8, 8px);
7
+ color: var(--color-fg-default);
8
+ background-image: linear-gradient(var(--color-accent-subtle), var(--color-accent-subtle));
9
+ border: var(--primer-borderWidth-thin, max(1px, 0.0625rem)) solid var(--color-accent-muted);
10
+ border-radius: var(--primer-borderRadius-medium, 6px);
11
+ grid-auto-flow: column;
12
+ grid-template-areas: 'visual message actions close';
13
+ grid-template-columns: min-content 1fr minmax(0,auto) min-content;
14
+ grid-template-rows: min-content;
15
+
16
+ /* `sm` breakpoint variantion */
17
+ @media (max-width: 543.98px) {
18
+ grid-template-areas:
19
+ 'visual message close'
20
+ '. actions actions';
21
+ grid-template-columns: min-content 1fr min-content;
22
+ grid-template-rows: min-content min-content;
23
+
24
+ & .Banner-actions {
25
+ margin: var(--base-size-8, 8px) 0 0 var(--base-size-8, 8px);
26
+ }
27
+ }
28
+
29
+ /* Elements */
30
+
31
+ & .Banner-visual {
32
+ display: grid;
33
+ padding: var(--base-size-6, 6px) var(--base-size-8, 8px);
34
+ grid-area: visual;
35
+ align-self: start;
36
+
37
+ & > .octicon {
38
+ margin-block: calc(var(--base-size-4, 4px) / 2);
39
+ }
40
+
41
+ & > * {
42
+ align-self: center;
43
+ }
44
+ }
45
+
46
+ & .Banner-message {
47
+ padding: var(--base-size-6, 6px) var(--base-size-8, 8px);
48
+ grid-area: message;
49
+ align-self: center;
50
+
51
+ & p:last-child {
52
+ margin-bottom: 0;
53
+ }
54
+
55
+ & .Banner-title:not(:only-child) {
56
+ margin-bottom: 0;
57
+ font-weight: var(--base-text-weight-semibold, 600);
58
+ }
59
+ }
60
+
61
+ & .Banner-actions {
62
+ grid-area: actions;
63
+
64
+ &:last-child {
65
+ align-self: center;
66
+ }
67
+ }
68
+
69
+ & .Banner-close {
70
+ grid-area: close;
71
+ margin-left: var(--primer-controlStack-medium-gap-condensed, 8px);
72
+ }
73
+
74
+ & .Banner-visual .octicon {
75
+ color: var(--color-accent-fg);
76
+ }
77
+
78
+ &.Banner--warning {
79
+ color: var(--color-fg-default);
80
+ background-image: linear-gradient(var(--color-attention-subtle), var(--color-attention-subtle));
81
+ border-color: var(--color-attention-muted);
82
+
83
+ & .Banner-visual .octicon {
84
+ color: var(--color-attention-fg);
85
+ }
86
+ }
87
+
88
+ &.Banner--error {
89
+ color: var(--color-fg-default);
90
+ background-image: linear-gradient(var(--color-danger-subtle), var(--color-danger-subtle));
91
+ border-color: var(--color-danger-muted);
92
+
93
+ & .Banner-visual .octicon {
94
+ color: var(--color-danger-fg);
95
+ }
96
+ }
97
+
98
+ &.Banner--success {
99
+ color: var(--color-fg-default);
100
+ background-image: linear-gradient(var(--color-success-subtle), var(--color-success-subtle));
101
+ border-color: var(--color-success-muted);
102
+
103
+ & .Banner-visual .octicon {
104
+ color: var(--color-success-fg);
105
+ }
106
+ }
107
+
108
+ /* Full-width */
109
+
110
+ &.Banner--full {
111
+ margin-top: calc(var(--primer-borderWidth-thin, max(1px, 0.0625rem)) * -1);
112
+ border-right: 0;
113
+ border-left: 0;
114
+ border-radius: 0;
115
+ }
116
+
117
+ /* --primer-viewportRange-narrowLandscape */
118
+ @media (max-width: 767.98px) {
119
+ &.Banner--full-whenNarrow {
120
+ margin-top: calc(var(--primer-borderWidth-thin, max(1px, 0.0625rem)) * -1);
121
+ border-right: 0;
122
+ border-left: 0;
123
+ border-radius: 0;
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Beta
5
+ # Use `Banner` to highlight important information.
6
+ class Banner < Primer::Component
7
+ status :beta
8
+
9
+ # A button or custom content that will render on the right-hand side of the component.
10
+ #
11
+ # To render a button, call the `with_action_button` method, which accepts the arguments accepted by <%= link_to_component(Primer::Beta::Button) %>.
12
+ #
13
+ # To render custom content, call the `with_action_content` method and pass a block that returns HTML.
14
+ renders_one :action, types: {
15
+ button: lambda { |**system_arguments|
16
+ deny_tag_argument(**system_arguments)
17
+
18
+ Primer::Beta::Button.new(**system_arguments)
19
+ },
20
+
21
+ content: lambda { |**system_arguments|
22
+ deny_tag_argument(**system_arguments)
23
+ system_arguments[:tag] = :div
24
+
25
+ Primer::BaseComponent.new(**system_arguments)
26
+ }
27
+ }
28
+
29
+ DEFAULT_SCHEME = :default
30
+ SCHEME_MAPPINGS = {
31
+ DEFAULT_SCHEME => "",
32
+ :warning => "Banner--warning",
33
+ :danger => "Banner--error",
34
+ :success => "Banner--success"
35
+ }.freeze
36
+
37
+ DEFAULT_ICONS = {
38
+ default: :bell,
39
+ warning: :alert,
40
+ danger: :stop,
41
+ success: :"check-circle"
42
+ }.freeze
43
+
44
+ # @example Schemes
45
+ # <div style="display: grid; row-gap: 15px">
46
+ # <%= render(Primer::Beta::Banner.new) { "This is a banner message!" } %>
47
+ # <%= render(Primer::Beta::Banner.new(scheme: :warning)) { "This is a warning banner!" } %>
48
+ # <%= render(Primer::Beta::Banner.new(scheme: :danger)) { "This is a danger banner!" } %>
49
+ # <%= render(Primer::Beta::Banner.new(scheme: :success)) { "This is a success banner!" } %>
50
+ # </div>
51
+ #
52
+ # @example Full width
53
+ # <%= render(Primer::Beta::Banner.new(full: true)) { "This is a full width banner!" } %>
54
+ #
55
+ # @example Dismissible
56
+ # <%= render(Primer::Beta::Banner.new(dismissible: true, reappear: true)) { "This is a dismissible banner!" } %>
57
+ #
58
+ # @example Custom icon
59
+ # <%= render(Primer::Beta::Banner.new(icon: :people)) { "This is a banner with a custom icon!" } %>
60
+ #
61
+ # @example With action button
62
+ # <%= render(Primer::Beta::Banner.new) do |component| %>
63
+ # This is a banner with an action button!
64
+ # <% component.with_action_button(size: :small) { "Take action" } %>
65
+ # <% end %>
66
+ #
67
+ # @example With custom action
68
+ # <%= render(Primer::Beta::Banner.new) do |component| %>
69
+ # Comment saved!
70
+ # <% component.with_action_content do %>
71
+ # <%= render(Primer::IconButton.new(icon: :pencil, mr: 1, "aria-label": "Edit")) %>
72
+ # <% end %>
73
+ # <% end %>
74
+ #
75
+ # @param full [Boolean] Whether the component should take up the full width of the screen.
76
+ # @param full_when_narrow [Boolean] Whether the component should take up the full width of the screen when rendered inside smaller viewports.
77
+ # @param dismissible [Boolean] Whether the component can be dismissed with an "x" button.
78
+ # @param description [String] Description text rendered underneath the message.
79
+ # @param icon [Symbol] The name of an <%= link_to_octicons %> icon to use. If no icon is provided, a default one will be chosen based on the scheme.
80
+ # @param scheme [Symbol] <%= one_of(Primer::Beta::Banner::SCHEME_MAPPINGS.keys) %>
81
+ # @param reappear [Boolean] Whether or not the flash banner should reappear after being dismissed. Only for use in test and preview environments.
82
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
83
+ def initialize(full: false, full_when_narrow: false, dismissible: false, description: nil, icon: nil, scheme: DEFAULT_SCHEME, reappear: false, **system_arguments)
84
+ @scheme = fetch_or_fallback(SCHEME_MAPPINGS.keys, scheme, DEFAULT_SCHEME)
85
+ @icon = icon || DEFAULT_ICONS[@scheme]
86
+ @dismissible = dismissible
87
+ @description = description
88
+ @reappear = reappear
89
+
90
+ @system_arguments = deny_tag_argument(**system_arguments)
91
+ @system_arguments[:tag] = :div
92
+ @system_arguments[:data] ||= {}
93
+ @system_arguments[:data][:target] = catalyst_target(field: "root")
94
+ @system_arguments[:data][:reappear] = @reappear if @reappear
95
+ @system_arguments[:classes] = class_names(
96
+ @system_arguments[:classes],
97
+ "Banner",
98
+ SCHEME_MAPPINGS[@scheme],
99
+ "Banner--full": full,
100
+ "Banner--full-whenNarrow": full_when_narrow
101
+ )
102
+
103
+ @message_arguments = {
104
+ tag: :div,
105
+ classes: "Banner-message"
106
+ }
107
+
108
+ @wrapper_arguments = {
109
+ tag: custom_element_name
110
+ }
111
+ end
112
+
113
+ private
114
+
115
+ def custom_element_name
116
+ "x-banner"
117
+ end
118
+
119
+ def catalyst_action(event:, function:)
120
+ "#{event}:#{custom_element_name}##{function}"
121
+ end
122
+
123
+ def catalyst_target(field:)
124
+ "#{custom_element_name}.#{field}"
125
+ end
126
+ end
127
+ end
128
+ end
@@ -20,7 +20,7 @@ module Primer
20
20
  # Use:
21
21
  #
22
22
  # - `visual_icon` for an <%= link_to_component(Primer::OcticonComponent) %>.
23
- # - `visual_image` for an <%= link_to_component(Primer::Image) %>.
23
+ # - `visual_image` for an <%= link_to_component(Primer::Alpha::Image) %>.
24
24
  # - `visual_spinner` for a <%= link_to_component(Primer::SpinnerComponent) %>.
25
25
  #
26
26
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
@@ -40,7 +40,7 @@ module Primer
40
40
  system_arguments[:size] = "56x56"
41
41
  system_arguments[:classes] = class_names("blankslate-image", system_arguments[:classes])
42
42
 
43
- Primer::Image.new(**system_arguments)
43
+ Primer::Alpha::Image.new(**system_arguments)
44
44
  }
45
45
  }
46
46
 
@@ -83,7 +83,7 @@ module Primer
83
83
  system_arguments[:size] = :medium
84
84
  system_arguments[:scheme] ||= :primary
85
85
 
86
- Primer::ButtonComponent.new(**system_arguments) # rubocop:disable Primer/ComponentNameMigration
86
+ Primer::ButtonComponent.new(**system_arguments)
87
87
  }
88
88
 
89
89
  # Optional secondary action
@@ -96,7 +96,7 @@ module Primer
96
96
  renders_one :secondary_action, lambda { |href:, **system_arguments|
97
97
  system_arguments[:href] = href
98
98
 
99
- Primer::LinkComponent.new(**system_arguments)
99
+ Primer::Beta::Link.new(**system_arguments)
100
100
  }
101
101
 
102
102
  # @example Basic
@@ -73,7 +73,7 @@ module Primer
73
73
  end
74
74
 
75
75
  render(Primer::BaseComponent.new(**@system_arguments)) do
76
- render(Primer::LinkComponent.new(**link_arguments)) { content }
76
+ render(Primer::Beta::Link.new(**link_arguments)) { content }
77
77
  end
78
78
  end
79
79
  end
@@ -57,7 +57,7 @@ module Primer
57
57
  # @param system_arguments [Hash] Same arguments as <%= link_to_component(Primer::Beta::Counter) %>.
58
58
  renders_one :trailing_visual, types: {
59
59
  icon: Primer::OcticonComponent,
60
- label: Primer::LabelComponent,
60
+ label: Primer::Beta::Label,
61
61
  counter: Primer::CounterComponent
62
62
  }
63
63