primer_view_components 0.0.19 → 0.0.24

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +78 -1
  3. data/app/assets/javascripts/primer_view_components.js +2 -0
  4. data/app/assets/javascripts/primer_view_components.js.map +1 -0
  5. data/app/components/primer/avatar_component.rb +7 -7
  6. data/app/components/primer/avatar_stack_component.rb +3 -3
  7. data/app/components/primer/base_component.rb +1 -1
  8. data/app/components/primer/blankslate_component.html.erb +3 -3
  9. data/app/components/primer/blankslate_component.rb +16 -24
  10. data/app/components/primer/border_box_component.html.erb +4 -18
  11. data/app/components/primer/border_box_component.rb +58 -67
  12. data/app/components/primer/box_component.rb +2 -2
  13. data/app/components/primer/breadcrumb_component.html.erb +1 -2
  14. data/app/components/primer/breadcrumb_component.rb +24 -13
  15. data/app/components/primer/button_component.rb +2 -2
  16. data/app/components/primer/button_group_component.rb +1 -1
  17. data/app/components/primer/button_marketing_component.rb +2 -2
  18. data/app/components/primer/component.rb +4 -0
  19. data/app/components/primer/counter_component.rb +1 -1
  20. data/app/components/primer/details_component.html.erb +2 -6
  21. data/app/components/primer/details_component.rb +22 -35
  22. data/app/components/primer/dropdown_component.html.erb +2 -2
  23. data/app/components/primer/dropdown_component.rb +4 -6
  24. data/app/components/primer/dropdown_menu_component.rb +4 -4
  25. data/app/components/primer/flash_component.html.erb +4 -7
  26. data/app/components/primer/flash_component.rb +16 -20
  27. data/app/components/primer/flex_component.rb +4 -4
  28. data/app/components/primer/flex_item_component.rb +1 -1
  29. data/app/components/primer/heading_component.rb +3 -1
  30. data/app/components/primer/label_component.rb +15 -25
  31. data/app/components/primer/layout_component.rb +2 -2
  32. data/app/components/primer/link_component.rb +2 -2
  33. data/app/components/primer/markdown_component.rb +8 -8
  34. data/app/components/primer/menu_component.rb +1 -1
  35. data/app/components/primer/octicon_component.rb +5 -3
  36. data/app/components/primer/popover_component.rb +3 -3
  37. data/app/components/primer/primer.js +1 -0
  38. data/app/components/primer/primer.ts +1 -0
  39. data/app/components/primer/progress_bar_component.html.erb +1 -1
  40. data/app/components/primer/progress_bar_component.rb +27 -31
  41. data/app/components/primer/spinner_component.rb +3 -3
  42. data/app/components/primer/state_component.rb +21 -10
  43. data/app/components/primer/subhead_component.html.erb +3 -15
  44. data/app/components/primer/subhead_component.rb +45 -61
  45. data/app/components/primer/tab_container_component.js +1 -0
  46. data/app/components/primer/tab_container_component.rb +41 -0
  47. data/app/components/primer/tab_container_component.ts +1 -0
  48. data/app/components/primer/tab_nav_component.html.erb +17 -0
  49. data/app/components/primer/tab_nav_component.rb +108 -0
  50. data/app/components/primer/text_component.rb +1 -1
  51. data/app/components/primer/timeline_item_component.html.erb +4 -16
  52. data/app/components/primer/timeline_item_component.rb +41 -52
  53. data/app/components/primer/tooltip_component.rb +5 -5
  54. data/app/components/primer/truncate_component.rb +4 -4
  55. data/app/components/primer/underline_nav_component.rb +2 -2
  56. data/{lib → app/lib}/primer/class_name_helper.rb +0 -0
  57. data/{lib → app/lib}/primer/classify.rb +1 -3
  58. data/{lib → app/lib}/primer/classify/cache.rb +0 -0
  59. data/{lib → app/lib}/primer/fetch_or_fallback_helper.rb +0 -0
  60. data/{lib → app/lib}/primer/join_style_arguments_helper.rb +0 -0
  61. data/app/lib/primer/view_helper.rb +22 -0
  62. data/app/lib/primer/view_helper/dsl.rb +34 -0
  63. data/lib/primer/view_components/engine.rb +10 -2
  64. data/lib/primer/view_components/version.rb +5 -1
  65. data/static/statuses.json +1 -1
  66. metadata +18 -8
  67. data/app/components/primer/view_components.rb +0 -60
@@ -0,0 +1 @@
1
+ import '@github/tab-container-element';
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use TabContainer to create tabbed content with keyboard support. This component does not add any styles.
5
+ # It only provides the tab functionality. If you want styled Tabs you can look at <%= link_to_component(Primer::TabNavComponent) %>.
6
+ #
7
+ # This component requires javascript.
8
+ class TabContainerComponent < Primer::Component
9
+ # @example Default
10
+ # <%= render(Primer::TabContainerComponent.new) do %>
11
+ # <div role="tablist">
12
+ # <button type="button" role="tab" aria-selected="true">Tab one</button>
13
+ # <button type="button" role="tab" tabindex="-1">Tab two</button>
14
+ # <button type="button" role="tab" tabindex="-1">Tab three</button>
15
+ # </div>
16
+ # <div role="tabpanel">
17
+ # Panel 1
18
+ # </div>
19
+ # <div role="tabpanel" hidden>
20
+ # Panel 2
21
+ # </div>
22
+ # <div role="tabpanel" hidden>
23
+ # Panel 3
24
+ # </div>
25
+ # <% end %>
26
+ #
27
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
28
+ def initialize(**system_arguments)
29
+ @system_arguments = system_arguments
30
+ @system_arguments[:tag] = "tab-container"
31
+ end
32
+
33
+ def call
34
+ render(Primer::BaseComponent.new(**@system_arguments)) { content }
35
+ end
36
+
37
+ def render?
38
+ content.present?
39
+ end
40
+ end
41
+ end
@@ -0,0 +1 @@
1
+ import '@github/tab-container-element'
@@ -0,0 +1,17 @@
1
+ <%= render wrapper.new(**@system_arguments) do %>
2
+ <nav role="tablist" aria-label="<%= @aria_label %>" class="tabnav-tabs">
3
+ <% tabs.each do |tab| %>
4
+ <%= tab %>
5
+ <% end %>
6
+ </nav >
7
+
8
+ <% if @with_panel %>
9
+ <% tabs.each do |tab| %>
10
+ <% if tab.panel.present? %>
11
+ <div role="tabpanel" <%= "hidden" if tab.hidden? %>>
12
+ <%= tab.panel %>
13
+ </div>
14
+ <% end %>
15
+ <% end %>
16
+ <% end %>
17
+ <% end %>
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use TabNav to style navigation with a tab-based selected state, typically used for navigation placed at the top of the page.
5
+ class TabNavComponent < Primer::Component
6
+ include ViewComponent::SlotableV2
7
+
8
+ class MultipleSelectedTabsError < StandardError; end
9
+ class NoSelectedTabsError < StandardError; end
10
+
11
+ # Tabs to be rendered.
12
+ #
13
+ # @param title [String] Text to be rendered by the tab.
14
+ # @param selected [Boolean] Whether the tab is selected.
15
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
16
+ renders_many :tabs, lambda { |**system_arguments|
17
+ return TabComponent.new(**system_arguments) unless @with_panel
18
+
19
+ TabComponent.new(tag: :button, type: :button, **system_arguments)
20
+ }
21
+
22
+ # @example Default
23
+ # <%= render(Primer::TabNavComponent.new) do |c| %>
24
+ # <% c.tab(selected: true, title: "Tab 1", href: "#") %>
25
+ # <% c.tab(title: "Tab 2", href: "#") %>
26
+ # <% c.tab(title: "Tab 3", href: "#") %>
27
+ # <% end %>
28
+ #
29
+ # @example With panels
30
+ # <%= render(Primer::TabNavComponent.new(with_panel: true)) do |c| %>
31
+ # <% c.tab(selected: true, title: "Tab 1") { "Panel 1" } %>
32
+ # <% c.tab(title: "Tab 2") { "Panel 1" } %>
33
+ # <% c.tab(title: "Tab 3") { "Panel 1" } %>
34
+ # <% end %>
35
+ #
36
+ # @param aria_label [String] Used to set the `aria-label` on the top level `<nav>` element.
37
+ # @param with_panel [Boolean] Whether the TabNav should navigate through pages or panels.
38
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
39
+ def initialize(aria_label: nil, with_panel: false, **system_arguments)
40
+ @aria_label = aria_label
41
+ @with_panel = with_panel
42
+ @system_arguments = system_arguments
43
+ @system_arguments[:tag] ||= :div
44
+
45
+ @system_arguments[:classes] = class_names(
46
+ "tabnav",
47
+ system_arguments[:classes]
48
+ )
49
+ end
50
+
51
+ def before_render
52
+ validate_single_selected_tab
53
+ end
54
+
55
+ private
56
+
57
+ def wrapper
58
+ @with_panel ? Primer::TabContainerComponent : Primer::BaseComponent
59
+ end
60
+
61
+ def validate_single_selected_tab
62
+ raise MultipleSelectedTabsError, "only one tab can be selected" if selected_tabs_count > 1
63
+ raise NoSelectedTabsError, "a tab must be selected" if selected_tabs_count != 1
64
+ end
65
+
66
+ def selected_tabs_count
67
+ @selected_tabs_count ||= tabs.count(&:selected)
68
+ end
69
+
70
+ # Tabs to be rendered.
71
+ class TabComponent < Primer::Component
72
+ attr_reader :selected
73
+
74
+ def initialize(title:, selected: false, **system_arguments)
75
+ @title = title
76
+ @selected = selected
77
+ @system_arguments = system_arguments
78
+ @system_arguments[:tag] ||= :a
79
+ @system_arguments[:role] = :tab
80
+
81
+ if selected
82
+ if @system_arguments[:tag] == :a
83
+ @system_arguments[:"aria-current"] = :page
84
+ else
85
+ @system_arguments[:"aria-selected"] = true
86
+ end
87
+ end
88
+
89
+ @system_arguments[:classes] = class_names(
90
+ "tabnav-tab",
91
+ system_arguments[:classes]
92
+ )
93
+ end
94
+
95
+ def call
96
+ render(Primer::BaseComponent.new(**@system_arguments)) { @title }
97
+ end
98
+
99
+ def panel
100
+ content
101
+ end
102
+
103
+ def hidden?
104
+ !@selected
105
+ end
106
+ end
107
+ end
108
+ end
@@ -3,7 +3,7 @@
3
3
  module Primer
4
4
  # The Text component is a wrapper component that will apply typography styles to the text inside.
5
5
  class TextComponent < Primer::Component
6
- # @example auto|Default
6
+ # @example Default
7
7
  # <%= render(Primer::TextComponent.new(tag: :p, font_weight: :bold)) { "Bold Text" } %>
8
8
  # <%= render(Primer::TextComponent.new(tag: :p, color: :red_5)) { "Red Text" } %>
9
9
  #
@@ -1,17 +1,5 @@
1
- <%= render Primer::BaseComponent.new(**system_arguments) do %>
2
- <% if avatar %>
3
- <%= render Primer::AvatarComponent.new(alt: avatar.alt, src: avatar.src, size: avatar.size, square: avatar.square, **avatar.system_arguments) %>
4
- <% end %>
5
-
6
- <% if badge %>
7
- <%= render Primer::BaseComponent.new(**badge.system_arguments) do %>
8
- <%= octicon badge.icon %>
9
- <% end %>
10
- <% end %>
11
-
12
- <% if body %>
13
- <%= render Primer::BaseComponent.new(**body.system_arguments) do %>
14
- <%= body.content %>
15
- <% end %>
16
- <% end %>
1
+ <%= render Primer::BaseComponent.new(**@system_arguments) do %>
2
+ <%= avatar %>
3
+ <%= badge %>
4
+ <%= body %>
17
5
  <% end %>
@@ -3,20 +3,45 @@
3
3
  module Primer
4
4
  # Use `TimelineItem` to display items on a vertical timeline, connected by badge elements.
5
5
  class TimelineItemComponent < Primer::Component
6
- include ViewComponent::Slotable
6
+ include ViewComponent::SlotableV2
7
7
 
8
- with_slot :avatar, class_name: "Avatar"
9
- with_slot :badge, class_name: "Badge"
10
- with_slot :body, class_name: "Body"
8
+ # Avatar to be rendered to the left of the Badge.
9
+ #
10
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::AvatarComponent) %>.
11
+ renders_one :avatar, lambda { |src:, size: 40, square: true, **system_arguments|
12
+ system_arguments[:classes] = class_names(
13
+ "TimelineItem-avatar",
14
+ system_arguments[:classes]
15
+ )
16
+
17
+ Primer::AvatarComponent.new(src: src, size: size, square: square, **system_arguments)
18
+ }
11
19
 
12
- attr_reader :system_arguments
20
+ # Badge that will be connected to other TimelineItems.
21
+ #
22
+ # @param icon [String] Name of [Octicon](https://primer.style/octicons/) to use.
23
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
24
+ renders_one :badge, "BadgeComponent"
13
25
 
14
- # @example auto|Default
26
+ # Body to be rendered to the left of the Badge.
27
+ #
28
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
29
+ renders_one :body, lambda { |**system_arguments|
30
+ system_arguments[:tag] = :div
31
+ system_arguments[:classes] = class_names(
32
+ "TimelineItem-body",
33
+ system_arguments[:classes]
34
+ )
35
+
36
+ Primer::BaseComponent.new(**system_arguments)
37
+ }
38
+
39
+ # @example Default
15
40
  # <div style="padding-left: 60px">
16
41
  # <%= render(Primer::TimelineItemComponent.new) do |component| %>
17
- # <% component.slot(:avatar, src: "https://github.com/github.png", alt: "github") %>
18
- # <% component.slot(:badge, bg: :green, color: :white, icon: :check) %>
19
- # <% component.slot(:body) { "Success!" } %>
42
+ # <% component.avatar(src: "https://github.com/github.png", alt: "github") %>
43
+ # <% component.badge(bg: :green, color: :white, icon: :check) %>
44
+ # <% component.body { "Success!" } %>
20
45
  # <% end %>
21
46
  # </div>
22
47
  #
@@ -36,36 +61,9 @@ module Primer
36
61
  avatar.present? || badge.present? || body.present?
37
62
  end
38
63
 
39
- # :nodoc:
40
- class Avatar < Primer::Slot
41
- attr_reader :system_arguments, :alt, :src, :size, :square
42
-
43
- # @param alt [String] Alt text for avatar image.
44
- # @param src [String] Src attribute for avatar image.
45
- # @param size [Integer] Image size.
46
- # @param square [Boolean] Whether to round the edges of the image.
47
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
48
- def initialize(alt: nil, src: nil, size: 40, square: true, **system_arguments)
49
- @alt = alt
50
- @src = src
51
- @size = size
52
- @square = square
53
-
54
- @system_arguments = system_arguments
55
- @system_arguments[:tag] = :div
56
- @system_arguments[:classes] = class_names(
57
- "TimelineItem-avatar",
58
- system_arguments[:classes]
59
- )
60
- end
61
- end
62
-
63
- # :nodoc:
64
- class Badge < Primer::Slot
65
- attr_reader :system_arguments, :icon
66
-
67
- # @param icon [String] Name of [Octicon](https://primer.style/octicons/) to use.
68
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
64
+ # This component is part of `Primer::TimelineItemComponent` and should not be
65
+ # used as a standalone component.
66
+ class BadgeComponent < Primer::Component
69
67
  def initialize(icon: nil, **system_arguments)
70
68
  @icon = icon
71
69
 
@@ -76,20 +74,11 @@ module Primer
76
74
  system_arguments[:classes]
77
75
  )
78
76
  end
79
- end
80
-
81
- # :nodoc:
82
- class Body < Primer::Slot
83
- attr_reader :system_arguments
84
77
 
85
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
86
- def initialize(**system_arguments)
87
- @system_arguments = system_arguments
88
- @system_arguments[:tag] = :div
89
- @system_arguments[:classes] = class_names(
90
- "TimelineItem-body",
91
- system_arguments[:classes]
92
- )
78
+ def call
79
+ render(Primer::BaseComponent.new(**@system_arguments)) do
80
+ render(Primer::OcticonComponent.new(icon: @icon))
81
+ end
93
82
  end
94
83
  end
95
84
  end
@@ -26,29 +26,29 @@ module Primer
26
26
  se
27
27
  ]
28
28
 
29
- # @example 55|Default
29
+ # @example Default
30
30
  # <div class="pt-5">
31
31
  # <%= render(Primer::TooltipComponent.new(label: "Even bolder")) { "Default Bold Text" } %>
32
32
  # </div>
33
33
  #
34
- # @example 65|Wrapping another component
34
+ # @example Wrapping another component
35
35
  # <div class="pt-5">
36
36
  # <%= render(Primer::TooltipComponent.new(label: "Even bolder")) do %>
37
37
  # <%= render(Primer::ButtonComponent.new) { "Bold Button" } %>
38
38
  # <% end %>
39
39
  # </div>
40
40
  #
41
- # @example 65|With a direction
41
+ # @example With a direction
42
42
  # <div class="pt-5">
43
43
  # <%= render(Primer::TooltipComponent.new(label: "Even bolder", direction: :s)) { "Bold Text With a Direction" } %>
44
44
  # </div>
45
45
  #
46
- # @example 65|With an alignment
46
+ # @example With an alignment
47
47
  # <div class="pt-5">
48
48
  # <%= render(Primer::TooltipComponent.new(label: "Even bolder", direction: :s, alignment: :right_1)) { "Bold Text With an Alignment" } %>
49
49
  # </div>
50
50
  #
51
- # @example 65|Without a delay
51
+ # @example Without a delay
52
52
  # <div class="pt-5">
53
53
  # <%= render(Primer::TooltipComponent.new(label: "Even bolder", direction: :s, no_delay: true)) { "Bold Text without a delay" } %>
54
54
  # </div>
@@ -3,18 +3,18 @@
3
3
  module Primer
4
4
  # Use TruncateComponent to shorten overflowing text with an ellipsis.
5
5
  class TruncateComponent < Primer::Component
6
- # @example auto|Default
6
+ # @example Default
7
7
  # <div class="col-2">
8
8
  # <%= render(Primer::TruncateComponent.new(tag: :p)) { "branch-name-that-is-really-long" } %>
9
9
  # </div>
10
10
  #
11
- # @example auto|Inline
11
+ # @example Inline
12
12
  # <%= render(Primer::TruncateComponent.new(tag: :span, inline: true)) { "branch-name-that-is-really-long" } %>
13
13
  #
14
- # @example auto|Expandable
14
+ # @example Expandable
15
15
  # <%= render(Primer::TruncateComponent.new(tag: :span, inline: true, expandable: true)) { "branch-name-that-is-really-long" } %>
16
16
  #
17
- # @example auto|Custom size
17
+ # @example Custom size
18
18
  # <%= render(Primer::TruncateComponent.new(tag: :span, inline: true, expandable: true, max_width: 100)) { "branch-name-that-is-really-long" } %>
19
19
  #
20
20
  # @param inline [Boolean] Whether the element is inline (or inline-block).
@@ -10,7 +10,7 @@ module Primer
10
10
 
11
11
  with_content_areas :body, :actions
12
12
 
13
- # @example auto|Default
13
+ # @example Default
14
14
  # <%= render(Primer::UnderlineNavComponent.new) do |component| %>
15
15
  # <% component.with(:body) do %>
16
16
  # <%= render(Primer::LinkComponent.new(href: "#url")) { "Item 1" } %>
@@ -20,7 +20,7 @@ module Primer
20
20
  # <% end %>
21
21
  # <% end %>
22
22
  #
23
- # @example auto|Align right
23
+ # @example Align right
24
24
  # <%= render(Primer::UnderlineNavComponent.new(align: :right)) do |component| %>
25
25
  # <% component.with(:body) do %>
26
26
  # <%= render(Primer::LinkComponent.new(href: "#url")) { "Item 1" } %>
File without changes
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "classify/cache"
4
-
5
3
  module Primer
6
4
  # :nodoc:
7
5
  class Classify
@@ -182,7 +180,7 @@ module Primer
182
180
  end
183
181
 
184
182
  def extract_value(memo, key, val, breakpoint)
185
- return if val.nil?
183
+ return if val.nil? || val == ""
186
184
 
187
185
  if SPACING_KEYS.include?(key)
188
186
  if MARGIN_DIRECTION_KEYS.include?(key)
File without changes
File without changes
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Module to allow shorthand calls for registered Primer components
5
+ #
6
+ # Registered components can be called with
7
+ # `primer(:name, **kwargs) { block }` instead of
8
+ # `render Primer::NameComponent.new(**kwargs) { block }`
9
+ module ViewHelper
10
+ extend ActiveSupport::Concern
11
+
12
+ class ViewHelperNotFound < StandardError; end
13
+
14
+ def primer(name, **component_args, &block)
15
+ component = Primer::Component.primer_helpers[name]
16
+
17
+ raise ViewHelperNotFound, "no component defined for helper #{name}" if component.blank?
18
+
19
+ render component.new(**component_args), &block
20
+ end
21
+ end
22
+ end