primer_view_components 0.0.44 → 0.0.45
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -0
- data/app/components/primer/{auto_complete/auto_component.d.ts → auto_complete_component.d.ts} +0 -0
- data/app/components/primer/{auto_complete/auto_component.js → auto_complete_component.js} +0 -0
- data/app/components/primer/base_component.rb +16 -16
- data/app/components/primer/clipboard_copy.rb +25 -7
- data/app/components/primer/details_menu_component.d.ts +1 -0
- data/app/components/primer/details_menu_component.js +1 -0
- data/app/lib/primer/classify.rb +17 -7
- data/app/lib/primer/classify/cache.rb +0 -20
- data/app/lib/primer/classify/utilities.rb +137 -0
- data/app/lib/primer/classify/utilities.yml +1147 -0
- data/lib/primer/view_components/linters/argument_mappers/system_arguments.rb +4 -2
- data/lib/primer/view_components/linters/button_component_migration_counter.rb +9 -5
- data/lib/primer/view_components/linters/helpers.rb +115 -17
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/tasks/utilities.rake +99 -0
- metadata +10 -6
- data/app/lib/primer/classify/spacing.rb +0 -63
@@ -7,7 +7,7 @@ module ERBLint
|
|
7
7
|
module ArgumentMappers
|
8
8
|
# Maps element attributes to system arguments.
|
9
9
|
class SystemArguments
|
10
|
-
|
10
|
+
STRING_PARAMETERS = %w[aria- data-].freeze
|
11
11
|
TEST_SELECTOR_REGEX = /test_selector\((?<selector>.+)\)$/.freeze
|
12
12
|
|
13
13
|
attr_reader :attribute
|
@@ -29,7 +29,9 @@ module ERBLint
|
|
29
29
|
{ test_selector: m[:selector].tr("'", '"') }
|
30
30
|
elsif attr_name == "data-test-selector"
|
31
31
|
{ test_selector: attribute.value.to_json }
|
32
|
-
elsif attr_name.start_with?(*
|
32
|
+
elsif attr_name.start_with?(*STRING_PARAMETERS)
|
33
|
+
raise ConversionError, "Cannot convert attribute \"#{attr_name}\" because its value contains an erb block" if attribute.value_node&.children&.any? { |n| n.try(:type) == :erb }
|
34
|
+
|
33
35
|
# if attribute has no value_node, it means it is a boolean attribute.
|
34
36
|
{ "\"#{attr_name}\"" => attribute.value_node ? attribute.value.to_json : true }
|
35
37
|
else
|
@@ -21,14 +21,18 @@ module ERBLint
|
|
21
21
|
nil
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
|
24
|
+
def correction(args)
|
25
|
+
return nil if args.nil?
|
26
26
|
|
27
|
+
correction = "<%= render Primer::ButtonComponent.new"
|
28
|
+
correction += "(#{args})" if args.present?
|
29
|
+
"#{correction} do %>"
|
30
|
+
end
|
31
|
+
|
32
|
+
def message(args)
|
27
33
|
return MESSAGE if args.nil?
|
28
34
|
|
29
|
-
|
30
|
-
msg += "(#{args})" if args.present?
|
31
|
-
"#{msg} %>\n\nInstead of:\n"
|
35
|
+
"#{MESSAGE}\n\nTry using:\n\n#{correction(args)}\n\nInstead of:\n"
|
32
36
|
end
|
33
37
|
end
|
34
38
|
end
|
@@ -7,19 +7,50 @@ module ERBLint
|
|
7
7
|
module Linters
|
8
8
|
# Helper methods for linting ERB.
|
9
9
|
module Helpers
|
10
|
+
# from https://github.com/Shopify/erb-lint/blob/6179ee2d9d681a6ec4dd02351a1e30eefa748d3d/lib/erb_lint/linters/self_closing_tag.rb
|
11
|
+
SELF_CLOSING_TAGS = %w[
|
12
|
+
area base br col command embed hr input keygen
|
13
|
+
link menuitem meta param source track wbr img
|
14
|
+
].freeze
|
15
|
+
|
10
16
|
def self.included(base)
|
11
17
|
base.include(ERBLint::LinterRegistry)
|
12
18
|
|
13
19
|
define_method "run" do |processed_source|
|
14
|
-
|
20
|
+
@offenses_not_corrected = 0
|
21
|
+
tags = tags(processed_source)
|
22
|
+
tag_tree = build_tag_tree(tags)
|
23
|
+
|
24
|
+
tags.each do |tag|
|
15
25
|
next if tag.closing?
|
16
26
|
next unless self.class::TAGS&.include?(tag.name)
|
17
27
|
|
18
|
-
classes = tag.attributes["class"]&.value&.split(" ")
|
28
|
+
classes = tag.attributes["class"]&.value&.split(" ") || []
|
29
|
+
|
30
|
+
tag_tree[tag][:offense] = false
|
19
31
|
|
20
|
-
next
|
32
|
+
next unless self.class::CLASSES.blank? || (classes & self.class::CLASSES).any?
|
21
33
|
|
22
|
-
|
34
|
+
args = map_arguments(tag)
|
35
|
+
correction = correction(args)
|
36
|
+
|
37
|
+
tag_tree[tag][:offense] = true
|
38
|
+
tag_tree[tag][:correctable] = !correction.nil?
|
39
|
+
tag_tree[tag][:message] = message(args)
|
40
|
+
tag_tree[tag][:correction] = correction
|
41
|
+
end
|
42
|
+
|
43
|
+
tag_tree.each do |tag, h|
|
44
|
+
next unless h[:offense]
|
45
|
+
|
46
|
+
# We always fix the offenses using blocks. The closing tag corresponds to `<% end %>`.
|
47
|
+
if h[:correctable]
|
48
|
+
add_offense(tag.loc, h[:message], h[:correction])
|
49
|
+
add_offense(h[:closing].loc, h[:message], "<% end %>")
|
50
|
+
else
|
51
|
+
@offenses_not_corrected += 1
|
52
|
+
generate_offense(self.class, processed_source, tag, h[:message])
|
53
|
+
end
|
23
54
|
end
|
24
55
|
|
25
56
|
counter_correct?(processed_source)
|
@@ -29,12 +60,10 @@ module ERBLint
|
|
29
60
|
return unless offense.context
|
30
61
|
|
31
62
|
lambda do |corrector|
|
32
|
-
if
|
33
|
-
|
34
|
-
corrector.replace(offense.source_range, offense.context)
|
63
|
+
if offense.context.include?(counter_disable)
|
64
|
+
correct_counter(corrector, processed_source, offense)
|
35
65
|
else
|
36
|
-
|
37
|
-
corrector.insert_before(processed_source.source_buffer.source_range, "#{offense.context}\n")
|
66
|
+
corrector.replace(offense.source_range, offense.context)
|
38
67
|
end
|
39
68
|
end
|
40
69
|
end
|
@@ -42,10 +71,77 @@ module ERBLint
|
|
42
71
|
|
43
72
|
private
|
44
73
|
|
74
|
+
# Override this function to convert the HTML element attributes to argument for a component.
|
75
|
+
#
|
76
|
+
# @return [Hash] if possible to map all attributes to arguments.
|
77
|
+
# @return [Nil] if cannot map to arguments.
|
78
|
+
def map_arguments(_tag)
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
|
82
|
+
# Override this function to define how to autocorrect an element to a component.
|
83
|
+
#
|
84
|
+
# @return [String] with the text to replace the HTML element if possible to correct.
|
85
|
+
# @return [Nil] if cannot correct element.
|
86
|
+
def correction(_tag)
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
|
90
|
+
# Override this function to customize the linter message.
|
91
|
+
#
|
92
|
+
# @return [String] message to show on linter error.
|
45
93
|
def message(_tag)
|
46
94
|
self.class::MESSAGE
|
47
95
|
end
|
48
96
|
|
97
|
+
def counter_disable
|
98
|
+
"erblint:counter #{self.class.name.demodulize}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def correct_counter(corrector, processed_source, offense)
|
102
|
+
if processed_source.file_content.include?(counter_disable)
|
103
|
+
# update the counter if exists
|
104
|
+
corrector.replace(offense.source_range, offense.context)
|
105
|
+
else
|
106
|
+
# add comment with counter if none
|
107
|
+
corrector.insert_before(processed_source.source_buffer.source_range, "#{offense.context}\n")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# This assumes that the AST provided represents valid HTML, where each tag has a corresponding closing tag.
|
112
|
+
# From the tags, we build a structured tree which represents the tag hierarchy.
|
113
|
+
# With this, we are able to know where the tags start and end.
|
114
|
+
def build_tag_tree(tags)
|
115
|
+
tag_tree = {}
|
116
|
+
current_opened_tag = nil
|
117
|
+
|
118
|
+
tags.each do |tag|
|
119
|
+
if tag.closing?
|
120
|
+
if current_opened_tag && tag.name == current_opened_tag.name
|
121
|
+
tag_tree[current_opened_tag][:closing] = tag
|
122
|
+
current_opened_tag = tag_tree[current_opened_tag][:parent]
|
123
|
+
end
|
124
|
+
|
125
|
+
next
|
126
|
+
end
|
127
|
+
|
128
|
+
self_closing = self_closing?(tag)
|
129
|
+
|
130
|
+
tag_tree[tag] = {
|
131
|
+
closing: self_closing ? tag : nil,
|
132
|
+
parent: current_opened_tag
|
133
|
+
}
|
134
|
+
|
135
|
+
current_opened_tag = tag unless self_closing
|
136
|
+
end
|
137
|
+
|
138
|
+
tag_tree
|
139
|
+
end
|
140
|
+
|
141
|
+
def self_closing?(tag)
|
142
|
+
tag.self_closing? || SELF_CLOSING_TAGS.include?(tag.name)
|
143
|
+
end
|
144
|
+
|
49
145
|
def tags(processed_source)
|
50
146
|
processed_source.parser.nodes_with_type(:tag).map { |tag_node| BetterHtml::Tree::Tag.from_node(tag_node) }
|
51
147
|
end
|
@@ -54,7 +150,6 @@ module ERBLint
|
|
54
150
|
comment_node = nil
|
55
151
|
expected_count = 0
|
56
152
|
rule_name = self.class.name.match(/:?:?(\w+)\Z/)[1]
|
57
|
-
offenses_count = @offenses.length
|
58
153
|
|
59
154
|
processed_source.parser.ast.descendants(:erb).each do |node|
|
60
155
|
indicator_node, _, code_node, = *node
|
@@ -62,29 +157,32 @@ module ERBLint
|
|
62
157
|
comment = code_node&.loc&.source&.strip
|
63
158
|
|
64
159
|
if indicator == "#" && comment.start_with?("erblint:count") && comment.match(rule_name)
|
65
|
-
comment_node =
|
160
|
+
comment_node = node
|
66
161
|
expected_count = comment.match(/\s(\d+)\s?$/)[1].to_i
|
67
162
|
end
|
68
163
|
end
|
69
164
|
|
70
|
-
if
|
71
|
-
|
165
|
+
if @offenses_not_corrected.zero?
|
166
|
+
# have to adjust to get `\n` so we delete the whole line
|
167
|
+
add_offense(processed_source.to_source_range(comment_node.loc.adjust(end_pos: 1)), "Unused erblint:count comment for #{rule_name}", "") if comment_node
|
72
168
|
return
|
73
169
|
end
|
74
170
|
|
75
171
|
first_offense = @offenses[0]
|
76
172
|
|
77
173
|
if comment_node.nil?
|
78
|
-
add_offense(processed_source.to_source_range(first_offense.source_range), "#{rule_name}: If you must, add <%# erblint:counter #{rule_name} #{
|
79
|
-
|
174
|
+
add_offense(processed_source.to_source_range(first_offense.source_range), "#{rule_name}: If you must, add <%# erblint:counter #{rule_name} #{@offenses_not_corrected} %> to bypass this check.", "<%# erblint:counter #{rule_name} #{@offenses_not_corrected} %>")
|
175
|
+
elsif expected_count != @offenses_not_corrected
|
176
|
+
add_offense(processed_source.to_source_range(comment_node.loc), "Incorrect erblint:counter number for #{rule_name}. Expected: #{expected_count}, actual: #{@offenses_not_corrected}.", "<%# erblint:counter #{rule_name} #{@offenses_not_corrected} %>")
|
177
|
+
# the only offenses remaining are not autocorrectable, so we can ignore them
|
178
|
+
elsif expected_count == @offenses_not_corrected && @offenses.size == @offenses_not_corrected
|
80
179
|
clear_offenses
|
81
|
-
add_offense(processed_source.to_source_range(comment_node.loc), "Incorrect erblint:counter number for #{rule_name}. Expected: #{expected_count}, actual: #{offenses_count}.", " erblint:counter #{rule_name} #{offenses_count} ") if expected_count != offenses_count
|
82
180
|
end
|
83
181
|
end
|
84
182
|
|
85
183
|
def generate_offense(klass, processed_source, tag, message = nil, replacement = nil)
|
86
184
|
message ||= klass::MESSAGE
|
87
|
-
klass_name = klass.name.
|
185
|
+
klass_name = klass.name.demodulize
|
88
186
|
offense = ["#{klass_name}:#{message}", tag.node.loc.source].join("\n")
|
89
187
|
add_offense(processed_source.to_source_range(tag.loc), offense, replacement)
|
90
188
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :utilities do
|
4
|
+
task :build do
|
5
|
+
require "yaml"
|
6
|
+
require "json"
|
7
|
+
require File.expand_path("./../../demo/config/environment.rb", __dir__)
|
8
|
+
|
9
|
+
# Keys that are looked for to be included in the utilities.yml file
|
10
|
+
SUPPORTED_KEYS = %i[
|
11
|
+
hide
|
12
|
+
float
|
13
|
+
m mt mr mb ml mx my
|
14
|
+
p pt pr pb pl px py
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
# Replacements for some classnames that end up being a different argument key
|
18
|
+
REPLACEMENT_KEYS = {
|
19
|
+
"^v-align" => "vertical_align",
|
20
|
+
"^d" => "display",
|
21
|
+
"^wb" => "word_break",
|
22
|
+
"^v" => "visibility"
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
BREAKPOINTS = [nil, "sm", "md", "lg", "xl"].freeze
|
26
|
+
|
27
|
+
css_data =
|
28
|
+
JSON.parse(
|
29
|
+
File.read(
|
30
|
+
File.join(
|
31
|
+
__FILE__.split("lib/tasks/utilities.rake")[0], "/node_modules/@primer/css/dist/stats/utilities.json"
|
32
|
+
)
|
33
|
+
)
|
34
|
+
)["selectors"]["values"]
|
35
|
+
|
36
|
+
output = {}
|
37
|
+
|
38
|
+
css_data.each do |selector|
|
39
|
+
selector.sub!(/^./, "")
|
40
|
+
# Next if selector has ancestors or sibling selectors
|
41
|
+
next if selector.match?(/[:><~\[\.]/)
|
42
|
+
next unless SUPPORTED_KEYS.any? { |key| selector.start_with?("#{key}-") }
|
43
|
+
|
44
|
+
# Dupe so we still have the selector at the end of slicing it up
|
45
|
+
classname = selector.dup
|
46
|
+
key = ""
|
47
|
+
|
48
|
+
# Look for a replacement key
|
49
|
+
REPLACEMENT_KEYS.each do |k, v|
|
50
|
+
next unless classname.match?(Regexp.new(k))
|
51
|
+
|
52
|
+
key = v
|
53
|
+
classname.sub!(Regexp.new(k + "-"), "")
|
54
|
+
end
|
55
|
+
|
56
|
+
# If we didn't find a replacement, grab the first text before hyphen
|
57
|
+
if classname == selector
|
58
|
+
key = classname.split("-").first
|
59
|
+
classname.sub!(/^[^-]+-/, "")
|
60
|
+
end
|
61
|
+
|
62
|
+
# Check if the next bit of the classname is a breakpoint
|
63
|
+
if classname.match?(/^(sm-|md-|lg-|xl-)/)
|
64
|
+
breakpoint = classname.split("-").first
|
65
|
+
classname.sub!(/^[^-]+-/, "")
|
66
|
+
end
|
67
|
+
|
68
|
+
# Change the rest from hypens to underscores
|
69
|
+
classname.sub!(/\-/, "_")
|
70
|
+
|
71
|
+
# convert padding/margin negative values ie n7 to -7
|
72
|
+
classname.sub!(/^n/, "-") if classname.match?(/^n[0-9]/)
|
73
|
+
|
74
|
+
key = key.to_sym
|
75
|
+
|
76
|
+
classname = if classname.match?(/\A[-+]?[0-9]+\z/)
|
77
|
+
classname.to_i
|
78
|
+
else
|
79
|
+
classname.to_sym
|
80
|
+
end
|
81
|
+
|
82
|
+
if output[key].nil?
|
83
|
+
output[key] = { classname => Array.new(5, nil) }
|
84
|
+
elsif output[key][classname].nil?
|
85
|
+
output[key][classname] = Array.new(5, nil)
|
86
|
+
end
|
87
|
+
|
88
|
+
output[key][classname][BREAKPOINTS.index(breakpoint)] = selector
|
89
|
+
end
|
90
|
+
|
91
|
+
output.transform_values! do |x|
|
92
|
+
x.transform_values { |y| y.reverse.drop_while(&:nil?).reverse }
|
93
|
+
end
|
94
|
+
|
95
|
+
File.open("app/lib/primer/classify/utilities.yml", "w") do |f|
|
96
|
+
f.puts YAML.dump(output)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: primer_view_components
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.45
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-06-
|
11
|
+
date: 2021-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionview
|
@@ -356,9 +356,9 @@ files:
|
|
356
356
|
- app/components/primer/auto_complete/auto_complete.html.erb
|
357
357
|
- app/components/primer/auto_complete/auto_complete.js
|
358
358
|
- app/components/primer/auto_complete/auto_complete.ts
|
359
|
-
- app/components/primer/auto_complete/auto_component.d.ts
|
360
|
-
- app/components/primer/auto_complete/auto_component.js
|
361
359
|
- app/components/primer/auto_complete/item.rb
|
360
|
+
- app/components/primer/auto_complete_component.d.ts
|
361
|
+
- app/components/primer/auto_complete_component.js
|
362
362
|
- app/components/primer/avatar_component.rb
|
363
363
|
- app/components/primer/avatar_stack_component.html.erb
|
364
364
|
- app/components/primer/avatar_stack_component.rb
|
@@ -386,6 +386,8 @@ files:
|
|
386
386
|
- app/components/primer/counter_component.rb
|
387
387
|
- app/components/primer/details_component.html.erb
|
388
388
|
- app/components/primer/details_component.rb
|
389
|
+
- app/components/primer/details_menu_component.d.ts
|
390
|
+
- app/components/primer/details_menu_component.js
|
389
391
|
- app/components/primer/dropdown.d.ts
|
390
392
|
- app/components/primer/dropdown.html.erb
|
391
393
|
- app/components/primer/dropdown.js
|
@@ -465,7 +467,8 @@ files:
|
|
465
467
|
- app/lib/primer/classify/functional_colors.rb
|
466
468
|
- app/lib/primer/classify/functional_text_colors.rb
|
467
469
|
- app/lib/primer/classify/grid.rb
|
468
|
-
- app/lib/primer/classify/
|
470
|
+
- app/lib/primer/classify/utilities.rb
|
471
|
+
- app/lib/primer/classify/utilities.yml
|
469
472
|
- app/lib/primer/fetch_or_fallback_helper.rb
|
470
473
|
- app/lib/primer/join_style_arguments_helper.rb
|
471
474
|
- app/lib/primer/octicon/cache.rb
|
@@ -486,6 +489,7 @@ files:
|
|
486
489
|
- lib/tasks/coverage.rake
|
487
490
|
- lib/tasks/docs.rake
|
488
491
|
- lib/tasks/statuses.rake
|
492
|
+
- lib/tasks/utilities.rake
|
489
493
|
- lib/yard/docs_helper.rb
|
490
494
|
- lib/yard/renders_many_handler.rb
|
491
495
|
- lib/yard/renders_one_handler.rb
|
@@ -510,7 +514,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
510
514
|
- !ruby/object:Gem::Version
|
511
515
|
version: '0'
|
512
516
|
requirements: []
|
513
|
-
rubygems_version: 3.
|
517
|
+
rubygems_version: 3.1.2
|
514
518
|
signing_key:
|
515
519
|
specification_version: 4
|
516
520
|
summary: ViewComponents for the Primer Design System
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Primer
|
4
|
-
class Classify
|
5
|
-
# Handler for PrimerCSS spacing classes.
|
6
|
-
class Spacing
|
7
|
-
BASE_OPTIONS = (0..6).to_a.freeze
|
8
|
-
BASE_MAPPINGS = {
|
9
|
-
my: BASE_OPTIONS,
|
10
|
-
pb: BASE_OPTIONS,
|
11
|
-
pl: BASE_OPTIONS,
|
12
|
-
pr: BASE_OPTIONS,
|
13
|
-
pt: BASE_OPTIONS,
|
14
|
-
px: BASE_OPTIONS,
|
15
|
-
py: BASE_OPTIONS
|
16
|
-
}.freeze
|
17
|
-
|
18
|
-
MARGIN_DIRECTION_OPTIONS = [*(-6..-1), *BASE_OPTIONS].freeze
|
19
|
-
MARGIN_DIRECTION_MAPPINGS = {
|
20
|
-
mb: MARGIN_DIRECTION_OPTIONS,
|
21
|
-
ml: MARGIN_DIRECTION_OPTIONS,
|
22
|
-
mr: MARGIN_DIRECTION_OPTIONS,
|
23
|
-
mt: MARGIN_DIRECTION_OPTIONS
|
24
|
-
}.freeze
|
25
|
-
|
26
|
-
AUTO_OPTIONS = [*BASE_OPTIONS, :auto].freeze
|
27
|
-
AUTO_MAPPINGS = {
|
28
|
-
m: AUTO_OPTIONS,
|
29
|
-
mx: AUTO_OPTIONS
|
30
|
-
}.freeze
|
31
|
-
|
32
|
-
RESPONSIVE_OPTIONS = [*BASE_OPTIONS, :responsive].freeze
|
33
|
-
RESPONSIVE_MAPPINGS = {
|
34
|
-
p: RESPONSIVE_OPTIONS
|
35
|
-
}.freeze
|
36
|
-
|
37
|
-
MAPPINGS = {
|
38
|
-
**BASE_MAPPINGS,
|
39
|
-
**MARGIN_DIRECTION_MAPPINGS,
|
40
|
-
**AUTO_MAPPINGS,
|
41
|
-
**RESPONSIVE_MAPPINGS
|
42
|
-
}.freeze
|
43
|
-
KEYS = MAPPINGS.keys.freeze
|
44
|
-
|
45
|
-
class << self
|
46
|
-
def spacing(key, val, breakpoint)
|
47
|
-
validate(key, val) unless Rails.env.production?
|
48
|
-
|
49
|
-
return "#{key.to_s.dasherize}#{breakpoint}-n#{val.abs}" if val.is_a?(Numeric) && val.negative?
|
50
|
-
|
51
|
-
"#{key.to_s.dasherize}#{breakpoint}-#{val.to_s.dasherize}"
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def validate(key, val)
|
57
|
-
raise ArgumentError, "#{key} is not a spacing key" unless KEYS.include?(key)
|
58
|
-
raise ArgumentError, "#{val} is not a valid value for :#{key}. Use one of #{MAPPINGS[key]}" unless MAPPINGS[key].include?(val)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|