primer_view_components 0.0.16 → 0.0.21

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/app/components/primer/avatar_component.rb +27 -9
  4. data/app/components/primer/avatar_stack_component.html.erb +10 -0
  5. data/app/components/primer/avatar_stack_component.rb +81 -0
  6. data/app/components/primer/base_component.rb +8 -4
  7. data/app/components/primer/blankslate_component.html.erb +1 -1
  8. data/app/components/primer/blankslate_component.rb +18 -25
  9. data/app/components/primer/border_box_component.rb +29 -13
  10. data/app/components/primer/box_component.rb +10 -0
  11. data/app/components/primer/breadcrumb_component.rb +3 -2
  12. data/app/components/primer/button_component.rb +3 -3
  13. data/app/components/primer/button_group_component.html.erb +5 -0
  14. data/app/components/primer/button_group_component.rb +37 -0
  15. data/app/components/primer/button_marketing_component.rb +73 -0
  16. data/app/components/primer/component.rb +13 -0
  17. data/app/components/primer/counter_component.rb +16 -9
  18. data/app/components/primer/details_component.rb +10 -6
  19. data/app/components/primer/dropdown/menu_component.html.erb +12 -0
  20. data/app/components/primer/dropdown/menu_component.rb +48 -0
  21. data/app/components/primer/dropdown_component.html.erb +9 -0
  22. data/app/components/primer/dropdown_component.rb +77 -0
  23. data/app/components/primer/dropdown_menu_component.rb +35 -3
  24. data/app/components/primer/flash_component.html.erb +2 -5
  25. data/app/components/primer/flash_component.rb +18 -19
  26. data/app/components/primer/flex_component.rb +47 -9
  27. data/app/components/primer/flex_item_component.rb +16 -1
  28. data/app/components/primer/heading_component.rb +7 -0
  29. data/app/components/primer/label_component.rb +6 -6
  30. data/app/components/primer/layout_component.rb +2 -2
  31. data/app/components/primer/link_component.rb +7 -3
  32. data/app/components/primer/markdown_component.rb +293 -0
  33. data/app/components/primer/menu_component.html.erb +6 -0
  34. data/app/components/primer/menu_component.rb +71 -0
  35. data/app/components/primer/octicon_component.rb +11 -6
  36. data/app/components/primer/popover_component.rb +6 -4
  37. data/app/components/primer/progress_bar_component.rb +9 -9
  38. data/app/components/primer/slot.rb +1 -0
  39. data/app/components/primer/spinner_component.rb +10 -7
  40. data/app/components/primer/state_component.rb +6 -6
  41. data/app/components/primer/subhead_component.rb +6 -3
  42. data/app/components/primer/text_component.rb +1 -1
  43. data/app/components/primer/timeline_item_component.html.erb +4 -16
  44. data/app/components/primer/timeline_item_component.rb +41 -49
  45. data/app/components/primer/tooltip_component.rb +88 -0
  46. data/app/components/primer/truncate_component.rb +41 -0
  47. data/app/components/primer/underline_nav_component.rb +26 -1
  48. data/app/components/primer/view_components.rb +9 -0
  49. data/lib/primer/class_name_helper.rb +1 -0
  50. data/lib/primer/classify.rb +139 -107
  51. data/lib/primer/classify/cache.rb +125 -0
  52. data/lib/primer/fetch_or_fallback_helper.rb +9 -0
  53. data/lib/primer/join_style_arguments_helper.rb +14 -0
  54. data/lib/primer/view_components.rb +32 -0
  55. data/lib/primer/view_components/engine.rb +1 -0
  56. data/lib/primer/view_components/version.rb +1 -1
  57. data/lib/yard/renders_many_handler.rb +19 -0
  58. data/lib/yard/renders_one_handler.rb +19 -0
  59. data/static/statuses.json +1 -0
  60. metadata +80 -19
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # The Tooltip component is a wrapper component that will apply a tooltip to the provided content.
5
+ class TooltipComponent < Primer::Component
6
+ DIRECTION_DEFAULT = :n
7
+ ALIGN_DEFAULT = :default
8
+ MULTILINE_DEFAULT = false
9
+ DELAY_DEFAULT = false
10
+
11
+ ALIGN_MAPPING = {
12
+ ALIGN_DEFAULT => "",
13
+ :left_1 => "tooltipped-align-left-1",
14
+ :right_1 => "tooltipped-align-right-1",
15
+ :left_2 => "tooltipped-align-left-2",
16
+ :right_2 => "tooltipped-align-right-2"
17
+ }.freeze
18
+
19
+ DIRECTION_OPTIONS = [DIRECTION_DEFAULT] + %i[
20
+ nw
21
+ ne
22
+ w
23
+ e
24
+ sw
25
+ s
26
+ se
27
+ ]
28
+
29
+ # @example 55|Default
30
+ # <div class="pt-5">
31
+ # <%= render(Primer::TooltipComponent.new(label: "Even bolder")) { "Default Bold Text" } %>
32
+ # </div>
33
+ #
34
+ # @example 65|Wrapping another component
35
+ # <div class="pt-5">
36
+ # <%= render(Primer::TooltipComponent.new(label: "Even bolder")) do %>
37
+ # <%= render(Primer::ButtonComponent.new) { "Bold Button" } %>
38
+ # <% end %>
39
+ # </div>
40
+ #
41
+ # @example 65|With a direction
42
+ # <div class="pt-5">
43
+ # <%= render(Primer::TooltipComponent.new(label: "Even bolder", direction: :s)) { "Bold Text With a Direction" } %>
44
+ # </div>
45
+ #
46
+ # @example 65|With an alignment
47
+ # <div class="pt-5">
48
+ # <%= render(Primer::TooltipComponent.new(label: "Even bolder", direction: :s, alignment: :right_1)) { "Bold Text With an Alignment" } %>
49
+ # </div>
50
+ #
51
+ # @example 65|Without a delay
52
+ # <div class="pt-5">
53
+ # <%= render(Primer::TooltipComponent.new(label: "Even bolder", direction: :s, no_delay: true)) { "Bold Text without a delay" } %>
54
+ # </div>
55
+ #
56
+ # @param label [String] the text to appear in the tooltip
57
+ # @param direction [String] Direction of the tooltip. <%= one_of(Primer::TooltipComponent::DIRECTION_OPTIONS) %>
58
+ # @param align [String] Align tooltips to the left or right of an element, combined with a `direction` to specify north or south. <%= one_of(Primer::TooltipComponent::ALIGN_MAPPING.keys) %>
59
+ # @param multiline [Boolean] Use this when you have long content
60
+ # @param no_delay [Boolean] By default the tooltips have a slight delay before appearing. Set true to override this
61
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
62
+ def initialize(
63
+ label:,
64
+ direction: DIRECTION_DEFAULT,
65
+ align: ALIGN_DEFAULT,
66
+ multiline: MULTILINE_DEFAULT,
67
+ no_delay: DELAY_DEFAULT,
68
+ **system_arguments
69
+ )
70
+ @system_arguments = system_arguments
71
+ @system_arguments[:tag] ||= :span
72
+ @system_arguments[:aria] = { label: label }
73
+
74
+ @system_arguments[:classes] = class_names(
75
+ @system_arguments[:classes],
76
+ "tooltipped",
77
+ "tooltipped-#{fetch_or_fallback(DIRECTION_OPTIONS, direction, DIRECTION_DEFAULT)}",
78
+ ALIGN_MAPPING[fetch_or_fallback(ALIGN_MAPPING.keys, align, ALIGN_DEFAULT)],
79
+ "tooltipped-no-delay" => fetch_or_fallback_boolean(no_delay, DELAY_DEFAULT),
80
+ "tooltipped-multiline" => fetch_or_fallback_boolean(multiline, MULTILINE_DEFAULT)
81
+ )
82
+ end
83
+
84
+ def call
85
+ render(Primer::BaseComponent.new(**@system_arguments)) { content }
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use TruncateComponent to shorten overflowing text with an ellipsis.
5
+ class TruncateComponent < Primer::Component
6
+ # @example auto|Default
7
+ # <div class="col-2">
8
+ # <%= render(Primer::TruncateComponent.new(tag: :p)) { "branch-name-that-is-really-long" } %>
9
+ # </div>
10
+ #
11
+ # @example auto|Inline
12
+ # <%= render(Primer::TruncateComponent.new(tag: :span, inline: true)) { "branch-name-that-is-really-long" } %>
13
+ #
14
+ # @example auto|Expandable
15
+ # <%= render(Primer::TruncateComponent.new(tag: :span, inline: true, expandable: true)) { "branch-name-that-is-really-long" } %>
16
+ #
17
+ # @example auto|Custom size
18
+ # <%= render(Primer::TruncateComponent.new(tag: :span, inline: true, expandable: true, max_width: 100)) { "branch-name-that-is-really-long" } %>
19
+ #
20
+ # @param inline [Boolean] Whether the element is inline (or inline-block).
21
+ # @param expandable [Boolean] Whether the entire string should be revealed on hover. Can only be used in conjunction with `inline`.
22
+ # @param max_width [Integer] Sets the max-width of the text.
23
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
24
+ def initialize(inline: false, expandable: false, max_width: nil, **system_arguments)
25
+ @system_arguments = system_arguments
26
+ @system_arguments[:tag] ||= :div
27
+ @system_arguments[:classes] = class_names(
28
+ @system_arguments[:classes],
29
+ "css-truncate",
30
+ "css-truncate-overflow" => !inline,
31
+ "css-truncate-target" => inline,
32
+ "expandable" => inline && expandable
33
+ )
34
+ @system_arguments[:style] = join_style_arguments(@system_arguments[:style], "max-width: #{max_width}px;") unless max_width.nil?
35
+ end
36
+
37
+ def call
38
+ render(Primer::BaseComponent.new(**@system_arguments)) { content }
39
+ end
40
+ end
41
+ end
@@ -1,12 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Primer
4
+ # Use the UnderlineNav component to style navigation with a minimal
5
+ # underlined selected state, typically used for navigation placed at the top
6
+ # of the page.
4
7
  class UnderlineNavComponent < Primer::Component
5
8
  ALIGN_DEFAULT = :left
6
- ALIGN_OPTIONS = [ALIGN_DEFAULT, :right]
9
+ ALIGN_OPTIONS = [ALIGN_DEFAULT, :right].freeze
7
10
 
8
11
  with_content_areas :body, :actions
9
12
 
13
+ # @example auto|Default
14
+ # <%= render(Primer::UnderlineNavComponent.new) do |component| %>
15
+ # <% component.with(:body) do %>
16
+ # <%= render(Primer::LinkComponent.new(href: "#url")) { "Item 1" } %>
17
+ # <% end %>
18
+ # <% component.with(:actions) do %>
19
+ # <%= render(Primer::ButtonComponent.new) { "Button!" } %>
20
+ # <% end %>
21
+ # <% end %>
22
+ #
23
+ # @example auto|Align right
24
+ # <%= render(Primer::UnderlineNavComponent.new(align: :right)) do |component| %>
25
+ # <% component.with(:body) do %>
26
+ # <%= render(Primer::LinkComponent.new(href: "#url")) { "Item 1" } %>
27
+ # <% end %>
28
+ # <% component.with(:actions) do %>
29
+ # <%= render(Primer::ButtonComponent.new) { "Button!" } %>
30
+ # <% end %>
31
+ # <% end %>
32
+ #
33
+ # @param align [Symbol] <%= one_of(Primer::UnderlineNavComponent::ALIGN_OPTIONS) %> - Defaults to <%= Primer::UnderlineNavComponent::ALIGN_DEFAULT %>
34
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
10
35
  def initialize(align: ALIGN_DEFAULT, **system_arguments)
11
36
  @align = fetch_or_fallback(ALIGN_OPTIONS, align, ALIGN_DEFAULT)
12
37
 
@@ -15,6 +15,7 @@ require "octicons_helper/helper"
15
15
  require "primer/class_name_helper"
16
16
  require "primer/classify"
17
17
  require "primer/fetch_or_fallback_helper"
18
+ require "primer/join_style_arguments_helper"
18
19
 
19
20
  # Base configurations
20
21
 
@@ -25,13 +26,17 @@ require_relative "slot"
25
26
  # Components
26
27
 
27
28
  require_relative "avatar_component"
29
+ require_relative "avatar_stack_component"
28
30
  require_relative "blankslate_component"
29
31
  require_relative "border_box_component"
30
32
  require_relative "box_component"
31
33
  require_relative "breadcrumb_component"
32
34
  require_relative "button_component"
35
+ require_relative "button_group_component"
36
+ require_relative "button_marketing_component"
33
37
  require_relative "counter_component"
34
38
  require_relative "details_component"
39
+ require_relative "dropdown_component"
35
40
  require_relative "dropdown_menu_component"
36
41
  require_relative "flash_component"
37
42
  require_relative "flex_component"
@@ -40,6 +45,8 @@ require_relative "heading_component"
40
45
  require_relative "label_component"
41
46
  require_relative "layout_component"
42
47
  require_relative "link_component"
48
+ require_relative "markdown_component"
49
+ require_relative "menu_component"
43
50
  require_relative "octicon_component"
44
51
  require_relative "popover_component"
45
52
  require_relative "progress_bar_component"
@@ -48,4 +55,6 @@ require_relative "state_component"
48
55
  require_relative "subhead_component"
49
56
  require_relative "text_component"
50
57
  require_relative "timeline_item_component"
58
+ require_relative "tooltip_component"
59
+ require_relative "truncate_component"
51
60
  require_relative "underline_nav_component"
@@ -4,6 +4,7 @@
4
4
  #
5
5
  # Helps build a list of conditional class names
6
6
  module Primer
7
+ # :nodoc:
7
8
  module ClassNameHelper
8
9
  def class_names(*args)
9
10
  classes = []
@@ -1,27 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "classify/cache"
4
+
3
5
  module Primer
6
+ # :nodoc:
4
7
  class Classify
5
- MARGIN_DIRECTION_KEYS = [:mt, :ml, :mb, :mr]
6
- SPACING_KEYS = ([:m, :my, :mx, :p, :py, :px, :pt, :pl, :pb, :pr] + MARGIN_DIRECTION_KEYS).freeze
8
+ MARGIN_DIRECTION_KEYS = %i[mt ml mb mr].freeze
9
+ SPACING_KEYS = (%i[m my mx p py px pt pl pb pr] + MARGIN_DIRECTION_KEYS).freeze
7
10
  DIRECTION_KEY = :direction
8
11
  JUSTIFY_CONTENT_KEY = :justify_content
9
12
  ALIGN_ITEMS_KEY = :align_items
10
13
  DISPLAY_KEY = :display
11
14
  RESPONSIVE_KEYS = ([DISPLAY_KEY, DIRECTION_KEY, JUSTIFY_CONTENT_KEY, ALIGN_ITEMS_KEY, :col, :float] + SPACING_KEYS).freeze
12
- BREAKPOINTS = ["", "-sm", "-md", "-lg", "-xl"]
15
+ BREAKPOINTS = ["", "-sm", "-md", "-lg", "-xl"].freeze
13
16
 
14
17
  # Keys where we can simply translate { key: value } into ".key-value"
15
- CONCAT_KEYS = SPACING_KEYS + [:hide, :position, :v, :float, :col, :text, :box_shadow].freeze
18
+ CONCAT_KEYS = SPACING_KEYS + %i[hide position v float col text box_shadow].freeze
16
19
 
17
20
  INVALID_CLASS_NAME_PREFIXES =
18
21
  (["bg-", "color-", "text-", "d-", "v-align-", "wb-", "text-", "box-shadow-"] + CONCAT_KEYS.map { |k| "#{k}-" }).freeze
22
+ FUNCTIONAL_COLOR_REGEX = /(primary|secondary|tertiary|link|success|warning|danger|info)/.freeze
19
23
 
20
24
  COLOR_KEY = :color
21
25
  BG_KEY = :bg
22
26
  VERTICAL_ALIGN_KEY = :vertical_align
23
27
  WORD_BREAK_KEY = :word_break
24
- TEXT_KEYS = [:text_align, :font_weight]
28
+ TEXT_KEYS = %i[text_align font_weight].freeze
25
29
  FLEX_KEY = :flex
26
30
  FLEX_GROW_KEY = :flex_grow
27
31
  FLEX_SHRINK_KEY = :flex_shrink
@@ -30,19 +34,20 @@ module Primer
30
34
  HEIGHT_KEY = :height
31
35
  BOX_SHADOW_KEY = :box_shadow
32
36
  VISIBILITY_KEY = :visibility
37
+ ANIMATION_KEY = :animation
33
38
 
34
39
  BOOLEAN_MAPPINGS = {
35
40
  underline: {
36
41
  mappings: [
37
42
  {
38
43
  value: true,
39
- css_class: "text-underline",
44
+ css_class: "text-underline"
40
45
  },
41
46
  {
42
47
  value: false,
43
- css_class: "no-underline",
44
- },
45
- ],
48
+ css_class: "no-underline"
49
+ }
50
+ ]
46
51
  },
47
52
  top: {
48
53
  mappings: [
@@ -77,8 +82,9 @@ module Primer
77
82
  ]
78
83
  }
79
84
  }.freeze
80
- BORDER_KEYS = [:border, :border_color].freeze
81
- BORDER_MARGIN_KEYS = [:border_top, :border_bottom, :border_left, :border_right].freeze
85
+ BORDER_KEYS = %i[border border_color].freeze
86
+ BORDER_MARGIN_KEYS = %i[border_top border_bottom border_left border_right].freeze
87
+ BORDER_RADIUS_KEY = :border_radius
82
88
  TYPOGRAPHY_KEYS = [:font_size].freeze
83
89
  VALID_KEYS = (
84
90
  CONCAT_KEYS +
@@ -88,6 +94,7 @@ module Primer
88
94
  TYPOGRAPHY_KEYS +
89
95
  TEXT_KEYS +
90
96
  [
97
+ BORDER_RADIUS_KEY,
91
98
  COLOR_KEY,
92
99
  BG_KEY,
93
100
  DISPLAY_KEY,
@@ -103,7 +110,8 @@ module Primer
103
110
  WIDTH_KEY,
104
111
  HEIGHT_KEY,
105
112
  BOX_SHADOW_KEY,
106
- VISIBILITY_KEY
113
+ VISIBILITY_KEY,
114
+ ANIMATION_KEY
107
115
  ]
108
116
  ).freeze
109
117
 
@@ -111,30 +119,31 @@ module Primer
111
119
  def call(classes: "", style: nil, **args)
112
120
  extracted_results = extract_hash(args)
113
121
 
114
- {
115
- class: [validated_class_names(classes), extracted_results[:classes]].compact.join(" ").presence,
116
- style: [extracted_results[:styles], style].compact.join("").presence,
117
- }.merge(extracted_results.except(:classes, :styles))
122
+ extracted_results[:class] = [
123
+ validated_class_names(classes),
124
+ extracted_results.delete(:classes)
125
+ ].compact.join(" ").presence
126
+
127
+ extracted_results[:style] = [
128
+ extracted_results.delete(:styles),
129
+ style
130
+ ].compact.join("").presence
131
+
132
+ extracted_results
118
133
  end
119
134
 
120
135
  private
121
136
 
122
137
  def validated_class_names(classes)
123
- return unless classes.present?
138
+ return if classes.blank?
124
139
 
125
140
  if ENV["RAILS_ENV"] == "development"
126
141
  invalid_class_names =
127
142
  classes.split(" ").each_with_object([]) do |class_name, memo|
128
- if INVALID_CLASS_NAME_PREFIXES.any? { |prefix| class_name.start_with?(prefix) }
129
- memo << class_name
130
- end
143
+ memo << class_name if INVALID_CLASS_NAME_PREFIXES.any? { |prefix| class_name.start_with?(prefix) }
131
144
  end
132
145
 
133
- if invalid_class_names.any?
134
- raise ArgumentError.new(
135
- "Use System Arguments (https://primer.style/view-components/system-arguments) instead of Primer CSS class #{'name'.pluralize(invalid_class_names.length)} #{invalid_class_names.to_sentence}. This warning will not be raised in production.",
136
- )
137
- end
146
+ raise ArgumentError, "Use System Arguments (https://primer.style/view-components/system-arguments) instead of Primer CSS class #{'name'.pluralize(invalid_class_names.length)} #{invalid_class_names.to_sentence}. This warning will not be raised in production." if invalid_class_names.any?
138
147
  end
139
148
 
140
149
  classes
@@ -152,99 +161,122 @@ module Primer
152
161
  # Example usage:
153
162
  # extract_hash({ mt: 4, py: 2 }) => "mt-4 py-2"
154
163
  def extract_hash(styles_hash)
155
- out = styles_hash.each_with_object({ classes: [], styles: [] }) do |(key, value), memo|
164
+ memo = { classes: [], styles: +"" }
165
+ styles_hash.each do |key, value|
156
166
  next unless VALID_KEYS.include?(key)
157
167
 
158
- if value.is_a?(Array) && !RESPONSIVE_KEYS.include?(key)
159
- raise ArgumentError, "#{key} does not support responsive values"
168
+ if value.is_a?(Array)
169
+ raise ArgumentError, "#{key} does not support responsive values" unless RESPONSIVE_KEYS.include?(key)
170
+
171
+ value.each_with_index do |val, index|
172
+ Primer::Classify::Cache.read(memo, key, val, BREAKPOINTS[index]) || extract_value(memo, key, val, BREAKPOINTS[index])
173
+ end
174
+ else
175
+ Primer::Classify::Cache.read(memo, key, value, BREAKPOINTS[0]) || extract_value(memo, key, value, BREAKPOINTS[0])
160
176
  end
177
+ end
161
178
 
162
- Array(value).each_with_index do |val, index|
163
- next if val.nil?
179
+ memo[:classes] = memo[:classes].join(" ")
164
180
 
165
- if SPACING_KEYS.include?(key)
166
- if MARGIN_DIRECTION_KEYS.include?(key)
167
- raise ArgumentError, "value of #{key} must be between -6 and 6" if (val < -6 || val > 6)
168
- elsif !((key == :mx || key == :my) && val == :auto)
169
- raise ArgumentError, "value of #{key} must be between 0 and 6" if (val < 0 || val > 6)
170
- end
171
- end
181
+ memo
182
+ end
183
+
184
+ def extract_value(memo, key, val, breakpoint)
185
+ return if val.nil? || val == ""
186
+
187
+ if SPACING_KEYS.include?(key)
188
+ if MARGIN_DIRECTION_KEYS.include?(key)
189
+ raise ArgumentError, "value of #{key} must be between -6 and 6" if val < -6 || val > 6
190
+ elsif !((key == :mx || key == :my) && val == :auto)
191
+ raise ArgumentError, "value of #{key} must be between 0 and 6" if val.negative? || val > 6
192
+ end
193
+ end
172
194
 
173
- dasherized_val = val.to_s.dasherize
174
- breakpoint = BREAKPOINTS[index]
175
-
176
- if BOOLEAN_MAPPINGS.has_key?(key)
177
- BOOLEAN_MAPPINGS[key][:mappings].map { |m| m[:css_class] if m[:value] == val }.compact.each do |css_class|
178
- memo[:classes] << css_class
179
- end
180
- elsif key == BG_KEY
181
- if val.to_s.starts_with?("#")
182
- memo[:styles] << "background-color: #{val};"
183
- else
184
- memo[:classes] << "bg-#{dasherized_val}"
185
- end
186
- elsif key == COLOR_KEY
187
- if val.to_s.chars.last !~ /\D/
188
- memo[:classes] << "color-#{dasherized_val}"
189
- else
190
- memo[:classes] << "text-#{dasherized_val}"
191
- end
192
- elsif key == DISPLAY_KEY
193
- memo[:classes] << "d#{breakpoint}-#{dasherized_val}"
194
- elsif key == VERTICAL_ALIGN_KEY
195
- memo[:classes] << "v-align-#{dasherized_val}"
196
- elsif key == WORD_BREAK_KEY
197
- memo[:classes] << "wb-#{dasherized_val}"
198
- elsif BORDER_KEYS.include?(key)
199
- memo[:classes] << "border-#{dasherized_val}"
200
- elsif BORDER_MARGIN_KEYS.include?(key)
201
- memo[:classes] << "#{key.to_s.dasherize}-#{val}"
202
- elsif key == DIRECTION_KEY
203
- memo[:classes] << "flex#{breakpoint}-#{dasherized_val}"
204
- elsif key == JUSTIFY_CONTENT_KEY
205
- formatted_value = val.to_s.gsub(/(flex\_|space\_)/, "")
206
- memo[:classes] << "flex#{breakpoint}-justify-#{formatted_value}"
207
- elsif key == ALIGN_ITEMS_KEY
208
- memo[:classes] << "flex#{breakpoint}-items-#{val.to_s.gsub("flex_", "")}"
209
- elsif key == FLEX_KEY
210
- memo[:classes] << "flex-#{val}"
211
- elsif key == FLEX_GROW_KEY
212
- memo[:classes] << "flex-grow-#{val}"
213
- elsif key == FLEX_SHRINK_KEY
214
- memo[:classes] << "flex-shrink-#{val}"
215
- elsif key == ALIGN_SELF_KEY
216
- memo[:classes] << "flex-self-#{val}"
217
- elsif key == WIDTH_KEY || key == HEIGHT_KEY
218
- if val == :fit || val == :fill
219
- memo[:classes] << "#{key}-#{val}"
220
- else
221
- memo[key] = val
222
- end
223
- elsif TEXT_KEYS.include?(key)
224
- memo[:classes] << "text-#{dasherized_val}"
225
- elsif TYPOGRAPHY_KEYS.include?(key)
226
- memo[:classes] << "f#{dasherized_val}"
227
- elsif MARGIN_DIRECTION_KEYS.include?(key) && val < 0
228
- memo[:classes] << "#{key.to_s.dasherize}#{breakpoint}-n#{val.abs}"
229
- elsif key == BOX_SHADOW_KEY
230
- if val == true
231
- memo[:classes] << "box-shadow"
232
- else
233
- memo[:classes] << "box-shadow-#{dasherized_val}"
234
- end
235
- elsif key == VISIBILITY_KEY
236
- memo[:classes] << "v-#{dasherized_val}"
195
+ if BOOLEAN_MAPPINGS.key?(key)
196
+ BOOLEAN_MAPPINGS[key][:mappings].map { |m| m[:css_class] if m[:value] == val }.compact.each do |css_class|
197
+ memo[:classes] << css_class
198
+ end
199
+ elsif key == BG_KEY
200
+ if val.to_s.start_with?("#")
201
+ memo[:styles] << "background-color: #{val};"
202
+ else
203
+ memo[:classes] << "bg-#{val.to_s.dasherize}"
204
+ end
205
+ elsif key == COLOR_KEY
206
+ char_code = val[-1].ord
207
+ # Does this string end in a character that is NOT a number?
208
+ memo[:classes] <<
209
+ if (char_code >= 48 && char_code <= 57) || # 48 is the charcode for 0; 57 is the charcode for 9
210
+ FUNCTIONAL_COLOR_REGEX.match?(val)
211
+ "color-#{val.to_s.dasherize}"
237
212
  else
238
- memo[:classes] << "#{key.to_s.dasherize}#{breakpoint}-#{dasherized_val}"
213
+ "text-#{val.to_s.dasherize}"
239
214
  end
215
+ elsif key == DISPLAY_KEY
216
+ memo[:classes] << "d#{breakpoint}-#{val.to_s.dasherize}"
217
+ elsif key == VERTICAL_ALIGN_KEY
218
+ memo[:classes] << "v-align-#{val.to_s.dasherize}"
219
+ elsif key == WORD_BREAK_KEY
220
+ memo[:classes] << "wb-#{val.to_s.dasherize}"
221
+ elsif BORDER_KEYS.include?(key)
222
+ border_value = if val == true
223
+ "border"
224
+ else
225
+ "border-#{val.to_s.dasherize}"
226
+ end
227
+
228
+ memo[:classes] << border_value
229
+ elsif BORDER_MARGIN_KEYS.include?(key)
230
+ memo[:classes] << "#{key.to_s.dasherize}-#{val}"
231
+ elsif key == BORDER_RADIUS_KEY
232
+ memo[:classes] << "rounded-#{val}"
233
+ elsif key == DIRECTION_KEY
234
+ memo[:classes] << "flex#{breakpoint}-#{val.to_s.dasherize}"
235
+ elsif key == JUSTIFY_CONTENT_KEY
236
+ formatted_value = val.to_s.gsub(/(flex\_|space\_)/, "")
237
+ memo[:classes] << "flex#{breakpoint}-justify-#{formatted_value}"
238
+ elsif key == ALIGN_ITEMS_KEY
239
+ memo[:classes] << "flex#{breakpoint}-items-#{val.to_s.gsub('flex_', '')}"
240
+ elsif key == FLEX_KEY
241
+ memo[:classes] << "flex-#{val}"
242
+ elsif key == FLEX_GROW_KEY
243
+ memo[:classes] << "flex-grow-#{val}"
244
+ elsif key == FLEX_SHRINK_KEY
245
+ memo[:classes] << "flex-shrink-#{val}"
246
+ elsif key == ALIGN_SELF_KEY
247
+ memo[:classes] << "flex-self-#{val}"
248
+ elsif key == WIDTH_KEY || key == HEIGHT_KEY
249
+ if val == :fit || val == :fill
250
+ memo[:classes] << "#{key}-#{val}"
251
+ else
252
+ memo[key] = val
240
253
  end
254
+ elsif TEXT_KEYS.include?(key)
255
+ memo[:classes] << "text-#{val.to_s.dasherize}"
256
+ elsif TYPOGRAPHY_KEYS.include?(key)
257
+ memo[:classes] << "f#{val.to_s.dasherize}"
258
+ elsif MARGIN_DIRECTION_KEYS.include?(key) && val.negative?
259
+ memo[:classes] << "#{key.to_s.dasherize}#{breakpoint}-n#{val.abs}"
260
+ elsif key == BOX_SHADOW_KEY
261
+ memo[:classes] << if val == true
262
+ "box-shadow"
263
+ else
264
+ "box-shadow-#{val.to_s.dasherize}"
265
+ end
266
+ elsif key == VISIBILITY_KEY
267
+ memo[:classes] << "v-#{val.to_s.dasherize}"
268
+ elsif key == ANIMATION_KEY
269
+ memo[:classes] << if val == :grow
270
+ "hover-grow"
271
+ else
272
+ "anim-#{val.to_s.dasherize}"
273
+ end
274
+ else
275
+ memo[:classes] << "#{key.to_s.dasherize}#{breakpoint}-#{val.to_s.dasherize}"
241
276
  end
242
-
243
- {
244
- classes: out[:classes].join(" "),
245
- styles: out[:styles].join(" ")
246
- }.merge(out.except(:classes, :styles))
247
277
  end
248
278
  end
279
+
280
+ Cache.preload!
249
281
  end
250
282
  end