primer_view_components 0.0.17 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +102 -0
  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 +27 -9
  6. data/app/components/primer/avatar_stack_component.html.erb +10 -0
  7. data/app/components/primer/avatar_stack_component.rb +81 -0
  8. data/app/components/primer/base_component.rb +8 -5
  9. data/app/components/primer/blankslate_component.html.erb +3 -3
  10. data/app/components/primer/blankslate_component.rb +18 -25
  11. data/app/components/primer/border_box_component.html.erb +4 -18
  12. data/app/components/primer/border_box_component.rb +75 -68
  13. data/app/components/primer/box_component.rb +10 -0
  14. data/app/components/primer/breadcrumb_component.rb +3 -2
  15. data/app/components/primer/button_component.rb +3 -3
  16. data/app/components/primer/button_group_component.html.erb +5 -0
  17. data/app/components/primer/button_group_component.rb +37 -0
  18. data/app/components/primer/button_marketing_component.rb +73 -0
  19. data/app/components/primer/component.rb +16 -0
  20. data/app/components/primer/counter_component.rb +16 -9
  21. data/app/components/primer/details_component.html.erb +2 -6
  22. data/app/components/primer/details_component.rb +28 -37
  23. data/app/components/primer/dropdown/menu_component.html.erb +12 -0
  24. data/app/components/primer/dropdown/menu_component.rb +48 -0
  25. data/app/components/primer/dropdown_component.html.erb +9 -0
  26. data/app/components/primer/dropdown_component.rb +75 -0
  27. data/app/components/primer/dropdown_menu_component.rb +35 -3
  28. data/app/components/primer/flash_component.html.erb +4 -7
  29. data/app/components/primer/flash_component.rb +18 -17
  30. data/app/components/primer/flex_component.rb +47 -9
  31. data/app/components/primer/flex_item_component.rb +16 -1
  32. data/app/components/primer/heading_component.rb +9 -0
  33. data/app/components/primer/label_component.rb +6 -6
  34. data/app/components/primer/layout_component.rb +2 -2
  35. data/app/components/primer/link_component.rb +6 -2
  36. data/app/components/primer/markdown_component.rb +293 -0
  37. data/app/components/primer/menu_component.html.erb +6 -0
  38. data/app/components/primer/menu_component.rb +71 -0
  39. data/app/components/primer/octicon_component.rb +13 -6
  40. data/app/components/primer/popover_component.rb +5 -3
  41. data/app/components/primer/primer.js +1 -0
  42. data/app/components/primer/primer.ts +1 -0
  43. data/app/components/primer/progress_bar_component.rb +6 -6
  44. data/app/components/primer/spinner_component.rb +8 -5
  45. data/app/components/primer/state_component.rb +23 -12
  46. data/app/components/primer/subhead_component.rb +6 -3
  47. data/app/components/primer/tab_container_component.js +1 -0
  48. data/app/components/primer/tab_container_component.rb +41 -0
  49. data/app/components/primer/tab_container_component.ts +1 -0
  50. data/app/components/primer/tab_nav_component.html.erb +17 -0
  51. data/app/components/primer/tab_nav_component.rb +108 -0
  52. data/app/components/primer/text_component.rb +1 -1
  53. data/app/components/primer/timeline_item_component.html.erb +4 -16
  54. data/app/components/primer/timeline_item_component.rb +41 -49
  55. data/app/components/primer/tooltip_component.rb +88 -0
  56. data/app/components/primer/truncate_component.rb +41 -0
  57. data/app/components/primer/underline_nav_component.rb +26 -1
  58. data/{lib → app/lib}/primer/class_name_helper.rb +1 -0
  59. data/app/lib/primer/classify.rb +280 -0
  60. data/app/lib/primer/classify/cache.rb +125 -0
  61. data/{lib → app/lib}/primer/fetch_or_fallback_helper.rb +1 -0
  62. data/{lib → app/lib}/primer/join_style_arguments_helper.rb +1 -0
  63. data/app/lib/primer/view_helper.rb +22 -0
  64. data/app/lib/primer/view_helper/dsl.rb +34 -0
  65. data/lib/primer/view_components.rb +32 -0
  66. data/lib/primer/view_components/engine.rb +11 -2
  67. data/lib/primer/view_components/version.rb +5 -1
  68. data/lib/yard/renders_many_handler.rb +19 -0
  69. data/lib/yard/renders_one_handler.rb +19 -0
  70. data/static/statuses.json +1 -0
  71. metadata +94 -24
  72. data/app/components/primer/view_components.rb +0 -52
  73. data/lib/primer/classify.rb +0 -250
@@ -1,26 +1,12 @@
1
1
  <%= render Primer::BaseComponent.new(**@system_arguments) do %>
2
- <% if header %>
3
- <%= render Primer::BaseComponent.new(**header.system_arguments) do %>
4
- <%= header.content %>
5
- <% end %>
6
- <% end %>
7
- <% if body %>
8
- <%= render Primer::BaseComponent.new(**body.system_arguments) do %>
9
- <%= body.content %>
10
- <% end %>
11
- <% end %>
2
+ <%= header %>
3
+ <%= body %>
12
4
  <% if rows.any? %>
13
5
  <ul>
14
6
  <% rows.each do |row| %>
15
- <%= render Primer::BaseComponent.new(**row.system_arguments) do %>
16
- <%= row.content %>
17
- <% end %>
7
+ <%= row %>
18
8
  <% end %>
19
9
  </ul>
20
10
  <% end %>
21
- <% if footer %>
22
- <%= render Primer::BaseComponent.new(**footer.system_arguments) do %>
23
- <%= footer.content %>
24
- <% end %>
25
- <% end %>
11
+ <%= footer %>
26
12
  <% end %>
@@ -3,87 +3,94 @@
3
3
  module Primer
4
4
  # BorderBox is a Box component with a border.
5
5
  class BorderBoxComponent < Primer::Component
6
- include ViewComponent::Slotable
6
+ include ViewComponent::SlotableV2
7
7
 
8
- with_slot :header, class_name: "Header"
9
- with_slot :body, class_name: "Body"
10
- with_slot :footer, class_name: "Footer"
11
- with_slot :row, collection: true, class_name: "Row"
8
+ # Optional Header.
9
+ #
10
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
11
+ renders_one :header, lambda { |**system_arguments|
12
+ system_arguments[:tag] = :div
13
+ system_arguments[:classes] = class_names(
14
+ "Box-header",
15
+ system_arguments[:classes]
16
+ )
12
17
 
13
- # @example 350|Header, body, rows, and footer
14
- # <%= render(Primer::BorderBoxComponent.new) do |component|
15
- # component.slot(:header) { "Header" }
16
- # component.slot(:body) { "Body" }
17
- # component.slot(:row) { "Row one" }
18
- # component.slot(:row) { "Row two" }
19
- # component.slot(:row) { "Row three" }
20
- # component.slot(:footer) { "Footer" }
21
- # end %>
18
+ Primer::BaseComponent.new(**system_arguments)
19
+ }
20
+
21
+ # Optional Body.
22
22
  #
23
23
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
24
- def initialize(**system_arguments)
25
- @system_arguments = system_arguments
26
- @system_arguments[:tag] = :div
27
- @system_arguments[:classes] = class_names(
28
- "Box",
24
+ renders_one :body, lambda { |**system_arguments|
25
+ system_arguments[:tag] = :div
26
+ system_arguments[:classes] = class_names(
27
+ "Box-body",
29
28
  system_arguments[:classes]
30
29
  )
31
- end
32
30
 
33
- def render?
34
- rows.any? || header.present? || body.present? || footer.present?
35
- end
31
+ Primer::BaseComponent.new(**system_arguments)
32
+ }
36
33
 
37
- class Header < Primer::Slot
38
- attr_reader :system_arguments
39
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
40
- def initialize(**system_arguments)
41
- @system_arguments = system_arguments
42
- @system_arguments[:tag] = :div
43
- @system_arguments[:classes] = class_names(
44
- "Box-header",
45
- system_arguments[:classes]
46
- )
47
- end
48
- end
34
+ # Optional Footer.
35
+ #
36
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
37
+ renders_one :footer, lambda { |**system_arguments|
38
+ system_arguments[:tag] = :div
39
+ system_arguments[:classes] = class_names(
40
+ "Box-footer",
41
+ system_arguments[:classes]
42
+ )
43
+
44
+ Primer::BaseComponent.new(**system_arguments)
45
+ }
46
+
47
+ # Use Rows to add rows with borders and maintain the same padding.
48
+ #
49
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
50
+ renders_many :rows, lambda { |**system_arguments|
51
+ system_arguments[:tag] = :li
52
+ system_arguments[:classes] = class_names(
53
+ "Box-row",
54
+ system_arguments[:classes]
55
+ )
56
+
57
+ Primer::BaseComponent.new(**system_arguments)
58
+ }
49
59
 
50
- class Body < Primer::Slot
51
- attr_reader :system_arguments
52
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
53
- def initialize(**system_arguments)
54
- @system_arguments = system_arguments
55
- @system_arguments[:tag] = :div
56
- @system_arguments[:classes] = class_names(
57
- "Box-body",
58
- system_arguments[:classes]
59
- )
60
- end
60
+ # @example Header, body, rows, and footer
61
+ # <%= render(Primer::BorderBoxComponent.new) do |component| %>
62
+ # <% component.header do %>
63
+ # Header
64
+ # <% end %>
65
+ # <% component.body do %>
66
+ # Body
67
+ # <% end %>
68
+ # <% component.row do %>
69
+ # <% if true %>
70
+ # Row one
71
+ # <% end %>
72
+ # <% end %>
73
+ # <% component.row do %>
74
+ # Row two
75
+ # <% end %>
76
+ # <% component.footer do %>
77
+ # Footer
78
+ # <% end %>
79
+ # <% end %>
80
+ #
81
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
82
+ def initialize(**system_arguments)
83
+ @system_arguments = system_arguments
84
+ @system_arguments[:tag] = :div
85
+ @system_arguments[:classes] = class_names("Box", system_arguments[:classes])
61
86
  end
62
87
 
63
- class Footer < Primer::Slot
64
- attr_reader :system_arguments
65
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
66
- def initialize(**system_arguments)
67
- @system_arguments = system_arguments
68
- @system_arguments[:tag] = :div
69
- @system_arguments[:classes] = class_names(
70
- "Box-footer",
71
- system_arguments[:classes]
72
- )
73
- end
88
+ def render?
89
+ rows.any? || header.present? || body.present? || footer.present?
74
90
  end
75
91
 
76
- class Row < Primer::Slot
77
- attr_reader :system_arguments
78
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
79
- def initialize(**system_arguments)
80
- @system_arguments = system_arguments
81
- @system_arguments[:tag] = :li
82
- @system_arguments[:classes] = class_names(
83
- "Box-row",
84
- system_arguments[:classes]
85
- )
86
- end
92
+ def self.status
93
+ Primer::Component::STATUSES[:beta]
87
94
  end
88
95
  end
89
96
  end
@@ -3,6 +3,12 @@
3
3
  module Primer
4
4
  # A basic wrapper component for most layout related needs.
5
5
  class BoxComponent < Primer::Component
6
+ # @example Default
7
+ # <%= render(Primer::BoxComponent.new) { "Your content here" } %>
8
+ #
9
+ # @example Color and padding
10
+ # <%= render(Primer::BoxComponent.new(bg: :gray, p: 3)) { "Hello world" } %>
11
+ #
6
12
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
7
13
  def initialize(**system_arguments)
8
14
  @system_arguments = system_arguments
@@ -12,5 +18,9 @@ module Primer
12
18
  def call
13
19
  render(Primer::BaseComponent.new(**@system_arguments)) { content }
14
20
  end
21
+
22
+ def self.status
23
+ Primer::Component::STATUSES[:stable]
24
+ end
15
25
  end
16
26
  end
@@ -7,7 +7,7 @@ module Primer
7
7
 
8
8
  with_slot :item, collection: true, class_name: "BreadcrumbItem"
9
9
 
10
- # @example 40|Basic
10
+ # @example Basic
11
11
  # <%= render(Primer::BreadcrumbComponent.new) do |component| %>
12
12
  # <% component.slot(:item, href: "/") do %>Home<% end %>
13
13
  # <% component.slot(:item, href: "/about") do %>About<% end %>
@@ -33,7 +33,8 @@ module Primer
33
33
  # @param selected [Boolean] Whether or not the item is selected and not rendered as a link.
34
34
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
35
35
  def initialize(href: nil, selected: false, **system_arguments)
36
- @href, @system_arguments = href, system_arguments
36
+ @href = href
37
+ @system_arguments = system_arguments
37
38
 
38
39
  @href = nil if selected
39
40
  @system_arguments[:tag] = :li
@@ -16,7 +16,7 @@ module Primer
16
16
  VARIANT_MAPPINGS = {
17
17
  :small => "btn-sm",
18
18
  DEFAULT_VARIANT => "",
19
- :large => "btn-large",
19
+ :large => "btn-large"
20
20
  }.freeze
21
21
  VARIANT_OPTIONS = VARIANT_MAPPINGS.keys
22
22
 
@@ -26,13 +26,13 @@ module Primer
26
26
  DEFAULT_TYPE = :button
27
27
  TYPE_OPTIONS = [DEFAULT_TYPE, :reset, :submit].freeze
28
28
 
29
- # @example 50|Button types
29
+ # @example Button types
30
30
  # <%= render(Primer::ButtonComponent.new) { "Default" } %>
31
31
  # <%= render(Primer::ButtonComponent.new(button_type: :primary)) { "Primary" } %>
32
32
  # <%= render(Primer::ButtonComponent.new(button_type: :danger)) { "Danger" } %>
33
33
  # <%= render(Primer::ButtonComponent.new(button_type: :outline)) { "Outline" } %>
34
34
  #
35
- # @example 50|Variants
35
+ # @example Variants
36
36
  # <%= render(Primer::ButtonComponent.new(variant: :small)) { "Small" } %>
37
37
  # <%= render(Primer::ButtonComponent.new(variant: :medium)) { "Medium" } %>
38
38
  # <%= render(Primer::ButtonComponent.new(variant: :large)) { "Large" } %>
@@ -0,0 +1,5 @@
1
+ <%= render Primer::BaseComponent.new(**@system_arguments) do %>
2
+ <% buttons.each do |button| %>
3
+ <%= button %>
4
+ <% end %>
5
+ <% end %>
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use ButtonGroupComponent to render a series of buttons.
5
+ class ButtonGroupComponent < Primer::Component
6
+ include ViewComponent::SlotableV2
7
+
8
+ # Required list of buttons to be rendered.
9
+ #
10
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::ButtonComponent) %>.
11
+ renders_many :buttons, ->(**kwargs) { Primer::ButtonComponent.new(group_item: true, **kwargs) }
12
+
13
+ # @example Default
14
+ # <%= render(Primer::ButtonGroupComponent.new) do |component|
15
+ # component.button { "Default" }
16
+ # component.button(button_type: :primary) { "Primary" }
17
+ # component.button(button_type: :danger) { "Danger" }
18
+ # component.button(button_type: :outline) { "Outline" }
19
+ # component.button(classes: "my-class") { "Custom class" }
20
+ # end %>
21
+ #
22
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
23
+ def initialize(**system_arguments)
24
+ @system_arguments = system_arguments
25
+ @system_arguments[:tag] ||= :div
26
+
27
+ @system_arguments[:classes] = class_names(
28
+ "BtnGroup",
29
+ system_arguments[:classes]
30
+ )
31
+ end
32
+
33
+ def render?
34
+ buttons.any?
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use buttons for actions (e.g. in forms). Use links for destinations, or moving from one page to another.
5
+ class ButtonMarketingComponent < Primer::Component
6
+ DEFAULT_BUTTON_TYPE = :default
7
+ BUTTON_TYPE_MAPPINGS = {
8
+ DEFAULT_BUTTON_TYPE => "",
9
+ :primary => "btn-primary-mktg",
10
+ :outline => "btn-outline-mktg",
11
+ :transparent => "btn-transparent"
12
+ }.freeze
13
+ BUTTON_TYPE_OPTIONS = BUTTON_TYPE_MAPPINGS.keys
14
+
15
+ DEFAULT_VARIANT = :default
16
+ VARIANT_MAPPINGS = {
17
+ DEFAULT_VARIANT => "",
18
+ :large => "btn-large-mktg"
19
+ }.freeze
20
+ VARIANT_OPTIONS = VARIANT_MAPPINGS.keys
21
+
22
+ DEFAULT_TAG = :button
23
+ TAG_OPTIONS = [DEFAULT_TAG, :a].freeze
24
+
25
+ DEFAULT_TYPE = :button
26
+ TYPE_OPTIONS = [DEFAULT_TYPE, :submit].freeze
27
+
28
+ # @example Button types
29
+ # <%= render(Primer::ButtonMarketingComponent.new(mr: 2)) { "Default" } %>
30
+ # <%= render(Primer::ButtonMarketingComponent.new(button_type: :primary, mr: 2)) { "Primary" } %>
31
+ # <%= render(Primer::ButtonMarketingComponent.new(button_type: :outline)) { "Outline" } %>
32
+ # <div class="bg-gray-dark">
33
+ # <%= render(Primer::ButtonMarketingComponent.new(button_type: :transparent)) { "Transparent" } %>
34
+ # </div>
35
+ #
36
+ # @example Sizes
37
+ # <%= render(Primer::ButtonMarketingComponent.new(mr: 2)) { "Default" } %>
38
+ # <%= render(Primer::ButtonMarketingComponent.new(variant: :large)) { "Large" } %>
39
+ #
40
+ # @param button_type [Symbol] <%= one_of(Primer::ButtonMarketingComponent::BUTTON_TYPE_OPTIONS) %>
41
+ # @param variant [Symbol] <%= one_of(Primer::ButtonMarketingComponent::VARIANT_OPTIONS) %>
42
+ # @param tag [Symbol] <%= one_of(Primer::ButtonMarketingComponent::TAG_OPTIONS) %>
43
+ # @param type [Symbol] <%= one_of(Primer::ButtonMarketingComponent::TYPE_OPTIONS) %>
44
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
45
+ def initialize(
46
+ button_type: DEFAULT_BUTTON_TYPE,
47
+ variant: DEFAULT_VARIANT,
48
+ tag: DEFAULT_TAG,
49
+ type: DEFAULT_TYPE,
50
+ **system_arguments
51
+ )
52
+ @system_arguments = system_arguments
53
+ @system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
54
+
55
+ if @system_arguments[:tag] == :a
56
+ @system_arguments[:role] = :button
57
+ else
58
+ @system_arguments[:type] = fetch_or_fallback(TYPE_OPTIONS, type, DEFAULT_TYPE)
59
+ end
60
+
61
+ @system_arguments[:classes] = class_names(
62
+ "btn-mktg",
63
+ BUTTON_TYPE_MAPPINGS[fetch_or_fallback(BUTTON_TYPE_OPTIONS, button_type, DEFAULT_BUTTON_TYPE)],
64
+ VARIANT_MAPPINGS[fetch_or_fallback(VARIANT_OPTIONS, variant, DEFAULT_VARIANT)],
65
+ system_arguments[:classes]
66
+ )
67
+ end
68
+
69
+ def call
70
+ render(Primer::BaseComponent.new(**@system_arguments)) { content }
71
+ end
72
+ end
73
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "octicons_helper/helper"
4
+
3
5
  module Primer
4
6
  # @private
5
7
  class Component < ViewComponent::Base
@@ -7,5 +9,19 @@ module Primer
7
9
  include FetchOrFallbackHelper
8
10
  include OcticonsHelper
9
11
  include JoinStyleArgumentsHelper
12
+ include ViewHelper::DSL
13
+ include ViewHelper
14
+
15
+ # sourced from https://primer.style/doctocat/usage/front-matter#status
16
+ STATUSES = {
17
+ alpha: :alpha,
18
+ beta: :beta,
19
+ stable: :stable,
20
+ deprecated: :deprecated
21
+ }.freeze
22
+
23
+ def self.status
24
+ STATUSES[:alpha]
25
+ end
10
26
  end
11
27
  end
@@ -7,11 +7,11 @@ module Primer
7
7
  SCHEME_MAPPINGS = {
8
8
  DEFAULT_SCHEME => "Counter",
9
9
  :gray => "Counter Counter--gray",
10
- :light_gray => "Counter Counter--gray-light",
10
+ :light_gray => "Counter Counter--gray-light"
11
11
  }.freeze
12
12
 
13
13
  #
14
- # @example 34|Default
14
+ # @example Default
15
15
  # <%= render(Primer::CounterComponent.new(count: 25)) %>
16
16
  #
17
17
  # @param count [Integer, Float::INFINITY, nil] The number to be displayed (e.x. # of issues, pull requests)
@@ -30,7 +30,12 @@ module Primer
30
30
  round: false,
31
31
  **system_arguments
32
32
  )
33
- @count, @limit, @hide_if_zero, @text, @round, @system_arguments = count, limit, hide_if_zero, text, round, system_arguments
33
+ @count = count
34
+ @limit = limit
35
+ @hide_if_zero = hide_if_zero
36
+ @text = text
37
+ @round = round
38
+ @system_arguments = system_arguments
34
39
 
35
40
  @has_limit = !@limit.nil?
36
41
  @system_arguments[:title] = title
@@ -39,15 +44,17 @@ module Primer
39
44
  @system_arguments[:classes],
40
45
  SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_MAPPINGS.keys, scheme, DEFAULT_SCHEME)]
41
46
  )
42
- if count == 0 && hide_if_zero
43
- @system_arguments[:hidden] = true
44
- end
47
+ @system_arguments[:hidden] = true if count == 0 && hide_if_zero # rubocop:disable Style/NumericPredicate
45
48
  end
46
49
 
47
50
  def call
48
51
  render(Primer::BaseComponent.new(**@system_arguments)) { value }
49
52
  end
50
53
 
54
+ def self.status
55
+ Primer::Component::STATUSES[:beta]
56
+ end
57
+
51
58
  private
52
59
 
53
60
  def title
@@ -60,7 +67,7 @@ module Primer
60
67
  else
61
68
  count = @count.to_i
62
69
  str = number_with_delimiter(@has_limit ? [count, @limit].min : count)
63
- str += "+" if (@has_limit && count > @limit)
70
+ str += "+" if @has_limit && count > @limit
64
71
  str
65
72
  end
66
73
  end
@@ -76,14 +83,14 @@ module Primer
76
83
  if @round
77
84
  count = @has_limit ? [@count.to_i, @limit].min : @count.to_i
78
85
  precision = count.between?(100_000, 999_999) ? 0 : 1
79
- units = {thousand: "k", million: "m", billion: "b"}
86
+ units = { thousand: "k", million: "m", billion: "b" }
80
87
  str = number_to_human(count, precision: precision, significant: false, units: units, format: "%n%u")
81
88
  else
82
89
  @count = @count.to_i
83
90
  str = number_with_delimiter(@has_limit ? [@count, @limit].min : @count)
84
91
  end
85
92
 
86
- str += "+" if (@has_limit && @count.to_i > @limit)
93
+ str += "+" if @has_limit && @count.to_i > @limit
87
94
  str
88
95
  end
89
96
  end