primer_view_components 0.0.46 → 0.0.50
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 +144 -0
- data/app/components/primer/base_component.rb +2 -2
- data/app/components/primer/beta/auto_complete.rb +159 -0
- data/app/components/primer/beta/auto_complete/auto_complete.d.ts +1 -0
- data/app/components/primer/{auto_complete → beta/auto_complete}/auto_complete.html.erb +0 -0
- data/app/components/primer/beta/auto_complete/auto_complete.js +1 -0
- data/app/components/primer/{auto_complete → beta/auto_complete}/auto_complete.ts +0 -0
- data/app/components/primer/beta/auto_complete/item.rb +44 -0
- data/app/components/primer/beta/avatar.rb +77 -0
- data/app/components/primer/{avatar_stack_component.html.erb → beta/avatar_stack.html.erb} +0 -0
- data/app/components/primer/beta/avatar_stack.rb +92 -0
- data/app/components/primer/clipboard_copy.html.erb +2 -2
- data/app/components/primer/component.rb +5 -1
- data/app/components/primer/details_component.rb +7 -7
- data/app/components/primer/image_crop.html.erb +4 -4
- data/app/components/primer/label_component.rb +13 -12
- data/app/components/primer/markdown.rb +9 -9
- data/app/components/primer/navigation/tab_component.rb +30 -2
- data/app/components/primer/popover_component.rb +6 -3
- data/app/components/primer/primer.d.ts +1 -1
- data/app/components/primer/primer.js +1 -1
- data/app/components/primer/primer.ts +1 -1
- data/app/components/primer/tab_nav_component.rb +4 -3
- data/app/components/primer/timeline_item_component.rb +2 -2
- data/app/components/primer/truncate.rb +6 -1
- data/app/components/primer/underline_nav_component.rb +4 -3
- data/app/lib/primer/octicon/cache.rb +1 -1
- data/lib/primer/classify.rb +4 -18
- data/lib/primer/classify/cache.rb +0 -5
- data/lib/primer/classify/utilities.rb +54 -22
- data/lib/primer/classify/utilities.yml +16 -0
- data/lib/primer/view_components.rb +34 -6
- data/lib/primer/view_components/constants.rb +55 -0
- data/lib/primer/view_components/linters/argument_mappers/base.rb +74 -0
- data/lib/primer/view_components/linters/argument_mappers/button.rb +32 -44
- data/lib/primer/view_components/linters/argument_mappers/clipboard_copy.rb +20 -0
- data/lib/primer/view_components/linters/argument_mappers/helpers/erb_block.rb +24 -0
- data/lib/primer/view_components/linters/argument_mappers/label.rb +50 -0
- data/lib/primer/view_components/linters/argument_mappers/system_arguments.rb +4 -1
- data/lib/primer/view_components/linters/autocorrectable.rb +30 -0
- data/lib/primer/view_components/linters/button_component_migration_counter.rb +9 -23
- data/lib/primer/view_components/linters/clipboard_copy_component_migration_counter.rb +21 -0
- data/lib/primer/view_components/linters/close_button_component_migration_counter.rb +16 -0
- data/lib/primer/view_components/linters/helpers.rb +56 -38
- data/lib/primer/view_components/linters/label_component_migration_counter.rb +25 -0
- data/lib/primer/view_components/statuses.rb +14 -0
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/rubocop/config/default.yml +12 -0
- data/lib/rubocop/cop/primer.rb +4 -0
- data/lib/rubocop/cop/primer/no_tag_memoize.rb +42 -0
- data/lib/rubocop/cop/primer/system_argument_instead_of_class.rb +75 -0
- data/lib/tasks/constants.rake +12 -0
- data/lib/tasks/docs.rake +87 -32
- data/lib/tasks/utilities.rake +4 -10
- data/lib/yard/docs_helper.rb +12 -3
- data/static/arguments.yml +973 -0
- data/static/assets/view-components.svg +18 -0
- data/static/classes.yml +174 -0
- data/static/constants.json +628 -0
- data/static/statuses.json +5 -5
- metadata +34 -13
- data/app/components/primer/auto_complete.rb +0 -157
- data/app/components/primer/auto_complete/item.rb +0 -42
- data/app/components/primer/avatar_component.rb +0 -75
- data/app/components/primer/avatar_stack_component.rb +0 -90
@@ -19,6 +19,7 @@ module Primer
|
|
19
19
|
# Use the tabs to list navigation items. When `with_panel` is set on the parent, a button is rendered for panel navigation. Otherwise,
|
20
20
|
# an anchor tag is rendered for page navigation. For more information, refer to <%= link_to_component(Primer::Navigation::TabComponent) %>.
|
21
21
|
#
|
22
|
+
# @param panel_id [String] Only applies if `with_panel` is `true`. Unique id of panel.
|
22
23
|
# @param selected [Boolean] Whether the tab is selected.
|
23
24
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
24
25
|
renders_many :tabs, lambda { |selected: false, **system_arguments|
|
@@ -38,7 +39,7 @@ module Primer
|
|
38
39
|
|
39
40
|
# Use actions for a call to action.
|
40
41
|
#
|
41
|
-
# @param tag [
|
42
|
+
# @param tag [Symbol] (Primer::UnderlineNavComponent::ACTIONS_TAG_DEFAULT) <%= one_of(Primer::UnderlineNavComponent::ACTIONS_TAG_OPTIONS) %>
|
42
43
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
43
44
|
renders_one :actions, lambda { |tag: ACTIONS_TAG_DEFAULT, **system_arguments|
|
44
45
|
system_arguments[:tag] = fetch_or_fallback(ACTIONS_TAG_OPTIONS, tag, ACTIONS_TAG_DEFAULT)
|
@@ -104,13 +105,13 @@ module Primer
|
|
104
105
|
#
|
105
106
|
# @example With panels
|
106
107
|
# <%= render(Primer::UnderlineNavComponent.new(label: "With panels", with_panel: true)) do |component| %>
|
107
|
-
# <% component.tab(selected: true) do |t| %>
|
108
|
+
# <% component.tab(selected: true, id: "tab-1", panel_id: "panel-1") do |t| %>
|
108
109
|
# <% t.text { "Item 1" } %>
|
109
110
|
# <% t.panel do %>
|
110
111
|
# Panel 1
|
111
112
|
# <% end %>
|
112
113
|
# <% end %>
|
113
|
-
# <% component.tab do |t| %>
|
114
|
+
# <% component.tab(id: "tab-2", panel_id: "panel-2") do |t| %>
|
114
115
|
# <% t.text { "Item 2" } %>
|
115
116
|
# <% t.panel do %>
|
116
117
|
# Panel 2
|
@@ -6,7 +6,7 @@ module Primer
|
|
6
6
|
class Cache
|
7
7
|
LOOKUP = {} # rubocop:disable Style/MutableConstant
|
8
8
|
# Preload the top 20 used icons.
|
9
|
-
PRELOADED_ICONS = [:alert, :check, :"chevron-down", :
|
9
|
+
PRELOADED_ICONS = [:alert, :check, :"chevron-down", :paste, :clock, :"dot-fill", :info, :"kebab-horizontal", :link, :lock, :mail, :pencil, :plus, :question, :repo, :search, :"shield-lock", :star, :trash, :x].freeze
|
10
10
|
|
11
11
|
class << self
|
12
12
|
def get_key(symbol:, size:, width: nil, height: nil)
|
data/lib/primer/classify.rb
CHANGED
@@ -11,16 +11,6 @@ require_relative "classify/utilities"
|
|
11
11
|
module Primer
|
12
12
|
# :nodoc:
|
13
13
|
class Classify
|
14
|
-
# Load the utilities.yml file.
|
15
|
-
# Disabling because we want to load symbols, strings, and integers from the .yml file
|
16
|
-
# rubocop:disable Security/YAMLLoad
|
17
|
-
UTILITIES = YAML.load(
|
18
|
-
File.read(
|
19
|
-
File.join(File.dirname(__FILE__), "./classify/utilities.yml")
|
20
|
-
)
|
21
|
-
).freeze
|
22
|
-
# rubocop:enable Security/YAMLLoad
|
23
|
-
|
24
14
|
# Keys where we can simply translate { key: value } into ".key-value"
|
25
15
|
CONCAT_KEYS = %i[text box_shadow].freeze
|
26
16
|
|
@@ -90,7 +80,7 @@ module Primer
|
|
90
80
|
BORDER_RADIUS_KEY = :border_radius
|
91
81
|
TYPOGRAPHY_KEYS = [:font_size].freeze
|
92
82
|
VALID_KEYS = (
|
93
|
-
UTILITIES.keys +
|
83
|
+
Primer::Classify::Utilities::UTILITIES.keys +
|
94
84
|
CONCAT_KEYS +
|
95
85
|
BOOLEAN_MAPPINGS.keys +
|
96
86
|
BORDER_MARGIN_KEYS +
|
@@ -180,7 +170,9 @@ module Primer
|
|
180
170
|
def extract_value(memo, key, val, breakpoint)
|
181
171
|
return if val.nil? || val == ""
|
182
172
|
|
183
|
-
if
|
173
|
+
if (key == WIDTH_KEY || key == HEIGHT_KEY) && !val.is_a?(Symbol)
|
174
|
+
memo[key] = val
|
175
|
+
elsif Primer::Classify::Utilities.supported_key?(key)
|
184
176
|
memo[:classes] << Primer::Classify::Utilities.classname(key, val, breakpoint)
|
185
177
|
elsif BOOLEAN_MAPPINGS.key?(key)
|
186
178
|
BOOLEAN_MAPPINGS[key][:mappings].each do |m|
|
@@ -212,12 +204,6 @@ module Primer
|
|
212
204
|
memo[:classes] << Primer::Classify::Flex.classes(key, val, breakpoint)
|
213
205
|
elsif Primer::Classify::Grid::KEYS.include?(key)
|
214
206
|
memo[:classes] << Primer::Classify::Grid.classes(key, val, breakpoint)
|
215
|
-
elsif key == WIDTH_KEY || key == HEIGHT_KEY
|
216
|
-
if val == :fit
|
217
|
-
memo[:classes] << "#{key}-#{val}"
|
218
|
-
else
|
219
|
-
memo[key] = val
|
220
|
-
end
|
221
207
|
elsif TEXT_KEYS.include?(key)
|
222
208
|
memo[:classes] << "text-#{val.to_s.dasherize}"
|
223
209
|
elsif TYPOGRAPHY_KEYS.include?(key)
|
@@ -95,11 +95,6 @@ module Primer
|
|
95
95
|
values: Primer::Classify::Flex::ALIGN_SELF_VALUES
|
96
96
|
)
|
97
97
|
|
98
|
-
preload(
|
99
|
-
keys: [Primer::Classify::WIDTH_KEY, Primer::Classify::HEIGHT_KEY],
|
100
|
-
values: [:fit]
|
101
|
-
)
|
102
|
-
|
103
98
|
preload(
|
104
99
|
keys: Primer::Classify::BOX_SHADOW_KEY,
|
105
100
|
values: [true, :small, :medium, :large, :extra_large, :none]
|
@@ -1,17 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "yaml"
|
4
|
+
|
3
5
|
# :nodoc:
|
4
6
|
module Primer
|
5
7
|
class Classify
|
6
8
|
# Handler for PrimerCSS utility classes loaded from utilities.rake
|
7
9
|
class Utilities
|
10
|
+
# Load the utilities.yml file.
|
11
|
+
# Disabling because we want to load symbols, strings, and integers from the .yml file
|
12
|
+
# rubocop:disable Security/YAMLLoad
|
13
|
+
UTILITIES = YAML.load(
|
14
|
+
File.read(
|
15
|
+
File.join(File.dirname(__FILE__), "./utilities.yml")
|
16
|
+
)
|
17
|
+
).freeze
|
18
|
+
# rubocop:enable Security/YAMLLoad
|
19
|
+
BREAKPOINTS = ["", "-sm", "-md", "-lg", "-xl"].freeze
|
20
|
+
|
21
|
+
# Replacements for some classnames that end up being a different argument key
|
22
|
+
REPLACEMENT_KEYS = {
|
23
|
+
"^anim" => "animation",
|
24
|
+
"^v-align" => "vertical_align",
|
25
|
+
"^d" => "display",
|
26
|
+
"^wb" => "word_break",
|
27
|
+
"^v" => "visibility"
|
28
|
+
}.freeze
|
29
|
+
|
8
30
|
class << self
|
9
31
|
def classname(key, val, breakpoint = "")
|
10
32
|
if (valid = validate(key, val, breakpoint))
|
11
33
|
valid
|
12
34
|
else
|
13
35
|
# Get selector
|
14
|
-
|
36
|
+
UTILITIES[key][val][BREAKPOINTS.index(breakpoint)]
|
15
37
|
end
|
16
38
|
end
|
17
39
|
|
@@ -19,14 +41,14 @@ module Primer
|
|
19
41
|
#
|
20
42
|
# returns Boolean
|
21
43
|
def supported_key?(key)
|
22
|
-
|
44
|
+
UTILITIES[key].present?
|
23
45
|
end
|
24
46
|
|
25
47
|
# Does the Utilitiy class support the given key and value
|
26
48
|
#
|
27
49
|
# returns Boolean
|
28
50
|
def supported_value?(key, val)
|
29
|
-
supported_key?(key) &&
|
51
|
+
supported_key?(key) && UTILITIES[key][val].present?
|
30
52
|
end
|
31
53
|
|
32
54
|
# Does the given selector exist in the utilities file
|
@@ -34,7 +56,7 @@ module Primer
|
|
34
56
|
# returns Boolean
|
35
57
|
def supported_selector?(selector)
|
36
58
|
# This method is too slow to run in production
|
37
|
-
return false if
|
59
|
+
return false if ENV["RAILS_ENV"] == "production"
|
38
60
|
|
39
61
|
find_selector(selector).present?
|
40
62
|
end
|
@@ -43,7 +65,7 @@ module Primer
|
|
43
65
|
#
|
44
66
|
# returns Boolean
|
45
67
|
def responsive?(key, val)
|
46
|
-
supported_value?(key, val) &&
|
68
|
+
supported_value?(key, val) && UTILITIES[key][val].count > 1
|
47
69
|
end
|
48
70
|
|
49
71
|
# Get the options for the given key
|
@@ -52,13 +74,13 @@ module Primer
|
|
52
74
|
def mappings(key)
|
53
75
|
return unless supported_key?(key)
|
54
76
|
|
55
|
-
|
77
|
+
UTILITIES[key].keys
|
56
78
|
end
|
57
79
|
|
58
80
|
# Extract hash from classes ie. "mr-1 mb-2 foo" => { mr: 1, mb: 2, classes: "foo" }
|
59
81
|
def classes_to_hash(classes)
|
60
82
|
# This method is too slow to run in production
|
61
|
-
return { classes: classes } if
|
83
|
+
return { classes: classes } if ENV["RAILS_ENV"] == "production"
|
62
84
|
|
63
85
|
obj = {}
|
64
86
|
classes = classes.split(" ")
|
@@ -93,38 +115,48 @@ module Primer
|
|
93
115
|
private
|
94
116
|
|
95
117
|
def find_selector(selector)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
118
|
+
key = infer_selector_key(selector)
|
119
|
+
value_hash = UTILITIES[key]
|
120
|
+
|
121
|
+
return nil if value_hash.blank?
|
122
|
+
|
123
|
+
# Each value hash will also contain an array of classnames for breakpoints
|
124
|
+
# Key argument `0`, classes `[ "mr-0", "mr-sm-0", "mr-md-0", "mr-lg-0", "mr-xl-0" ]`
|
125
|
+
value_hash.each do |key_argument, classnames|
|
126
|
+
# Skip each value hash until we get one with the selector
|
127
|
+
next unless classnames.include?(selector)
|
128
|
+
|
129
|
+
# Return [:mr, 0, 1]
|
130
|
+
# has index of classname, so we can match it up with responsvie array `mr: [nil, 0]`
|
131
|
+
return [key, key_argument, classnames.index(selector)]
|
108
132
|
end
|
109
133
|
|
110
134
|
nil
|
111
135
|
end
|
112
136
|
|
137
|
+
def infer_selector_key(selector)
|
138
|
+
REPLACEMENT_KEYS.each do |k, v|
|
139
|
+
return v.to_sym if selector.match?(Regexp.new(k))
|
140
|
+
end
|
141
|
+
|
142
|
+
selector.split("-").first.to_sym
|
143
|
+
end
|
144
|
+
|
113
145
|
def validate(key, val, breakpoint)
|
114
146
|
unless supported_key?(key)
|
115
|
-
raise ArgumentError, "#{key} is not a valid Primer utility key" unless
|
147
|
+
raise ArgumentError, "#{key} is not a valid Primer utility key" unless ENV["RAILS_ENV"] == "production"
|
116
148
|
|
117
149
|
return ""
|
118
150
|
end
|
119
151
|
|
120
152
|
unless breakpoint.empty? || responsive?(key, val)
|
121
|
-
raise ArgumentError, "#{key} does not support responsive values" unless
|
153
|
+
raise ArgumentError, "#{key} does not support responsive values" unless ENV["RAILS_ENV"] == "production"
|
122
154
|
|
123
155
|
return ""
|
124
156
|
end
|
125
157
|
|
126
158
|
unless supported_value?(key, val)
|
127
|
-
raise ArgumentError, "#{val} is not a valid value for :#{key}. Use one of #{mappings(key)}" unless
|
159
|
+
raise ArgumentError, "#{val} is not a valid value for :#{key}. Use one of #{mappings(key)}" unless ENV["RAILS_ENV"] == "production"
|
128
160
|
|
129
161
|
return ""
|
130
162
|
end
|
@@ -85,6 +85,22 @@
|
|
85
85
|
- float-md-none
|
86
86
|
- float-lg-none
|
87
87
|
- float-xl-none
|
88
|
+
:width:
|
89
|
+
:fit:
|
90
|
+
- width-fit
|
91
|
+
:full:
|
92
|
+
- width-full
|
93
|
+
:auto:
|
94
|
+
- width-auto
|
95
|
+
- width-sm-auto
|
96
|
+
- width-md-auto
|
97
|
+
- width-lg-auto
|
98
|
+
- width-xl-auto
|
99
|
+
:height:
|
100
|
+
:fit:
|
101
|
+
- height-fit
|
102
|
+
:full:
|
103
|
+
- height-full
|
88
104
|
:m:
|
89
105
|
0:
|
90
106
|
- m-0
|
@@ -7,22 +7,21 @@ require "primer/view_components/engine"
|
|
7
7
|
module Primer
|
8
8
|
# :nodoc:
|
9
9
|
module ViewComponents
|
10
|
-
|
10
|
+
DEFAULT_STATIC_PATH = File.expand_path("static")
|
11
11
|
DEFAULT_STATUS_FILE_NAME = "statuses.json"
|
12
|
+
DEFAULT_CONSTANTS_FILE_NAME = "constants.json"
|
12
13
|
|
13
14
|
# generate_statuses returns a hash mapping component name to
|
14
15
|
# the component's status sorted alphabetically by the component name.
|
15
16
|
def self.generate_statuses
|
16
|
-
|
17
|
+
Primer::Component.descendants.sort_by(&:name).each_with_object({}) do |component, mem|
|
17
18
|
mem[component.to_s] = component.status.to_s
|
18
19
|
end
|
19
|
-
|
20
|
-
statuses.sort_by { |k, _v| k }.to_h
|
21
20
|
end
|
22
21
|
|
23
22
|
# dump_statuses generates the status hash and then serializes
|
24
23
|
# it as json at the given path
|
25
|
-
def self.dump_statuses(path:
|
24
|
+
def self.dump_statuses(path: DEFAULT_STATIC_PATH)
|
26
25
|
require "json"
|
27
26
|
|
28
27
|
statuses = generate_statuses
|
@@ -35,8 +34,37 @@ module Primer
|
|
35
34
|
|
36
35
|
# read_statuses returns a JSON string matching the output of
|
37
36
|
# generate_statuses
|
38
|
-
def self.read_statuses(path:
|
37
|
+
def self.read_statuses(path: DEFAULT_STATIC_PATH)
|
39
38
|
File.read(File.join(path, DEFAULT_STATUS_FILE_NAME))
|
40
39
|
end
|
40
|
+
|
41
|
+
# generate_constants returns a hash mapping component name to
|
42
|
+
# all of its constants.
|
43
|
+
def self.generate_constants
|
44
|
+
Primer::Component.descendants.sort_by(&:name).each_with_object({}) do |component, mem|
|
45
|
+
mem[component.to_s] = component.constants(false).sort.each_with_object({}) do |constant, h|
|
46
|
+
h[constant] = component.const_get(constant)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# dump_constants generates the constants hash and then serializes
|
52
|
+
# it as json at the given path
|
53
|
+
def self.dump_constants(path: DEFAULT_STATIC_PATH)
|
54
|
+
require "json"
|
55
|
+
|
56
|
+
constants = generate_constants
|
57
|
+
|
58
|
+
File.open(File.join(path, DEFAULT_CONSTANTS_FILE_NAME), "w") do |f|
|
59
|
+
f.write(JSON.pretty_generate(constants))
|
60
|
+
f.write($INPUT_RECORD_SEPARATOR)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# read_constants returns a JSON string matching the output of
|
65
|
+
# generate_constants
|
66
|
+
def self.read_constants(path: DEFAULT_STATIC_PATH)
|
67
|
+
File.read(File.join(path, DEFAULT_CONSTANTS_FILE_NAME))
|
68
|
+
end
|
41
69
|
end
|
42
70
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Primer
|
6
|
+
module ViewComponents
|
7
|
+
# A module for constants that are used in the view components.
|
8
|
+
class Constants
|
9
|
+
CONSTANTS = JSON.parse(
|
10
|
+
File.read(
|
11
|
+
File.join(File.dirname(__FILE__), "../../../static/constants.json")
|
12
|
+
)
|
13
|
+
).freeze
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def get(component:, constant:, invert: true, symbolize: false)
|
17
|
+
values = CONSTANTS.dig(component, constant)
|
18
|
+
|
19
|
+
case values
|
20
|
+
when Hash
|
21
|
+
format_hash(values, invert, symbolize)
|
22
|
+
when Array
|
23
|
+
format_array(values, symbolize)
|
24
|
+
else
|
25
|
+
values
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def format_hash(values, invert, symbolize)
|
32
|
+
val = values.invert if invert
|
33
|
+
# remove defaults
|
34
|
+
val = val.except("", nil)
|
35
|
+
|
36
|
+
return val.transform_values { |v| symbolize_value(v) } if symbolize
|
37
|
+
|
38
|
+
val
|
39
|
+
end
|
40
|
+
|
41
|
+
def format_array(values, symbolize)
|
42
|
+
val = values.select(&:present?)
|
43
|
+
|
44
|
+
return val.map { |v| symbolize_value(v) } if symbolize
|
45
|
+
|
46
|
+
val
|
47
|
+
end
|
48
|
+
|
49
|
+
def symbolize_value(value)
|
50
|
+
":#{value}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "primer/view_components/constants"
|
4
|
+
require "primer/classify/utilities"
|
5
|
+
require_relative "conversion_error"
|
6
|
+
require_relative "system_arguments"
|
7
|
+
require_relative "helpers/erb_block"
|
8
|
+
|
9
|
+
module ERBLint
|
10
|
+
module Linters
|
11
|
+
module ArgumentMappers
|
12
|
+
# Provides the base interface to implement an `ArgumentMapper`.
|
13
|
+
# Override attribute_to_args in a child class to customize its mapping behavior.
|
14
|
+
class Base
|
15
|
+
DEFAULT_TAG = nil
|
16
|
+
ATTRIBUTES = [].freeze
|
17
|
+
|
18
|
+
def initialize(tag)
|
19
|
+
@tag = tag
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
to_args.map { |k, v| "#{k}: #{v}" }.join(", ")
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_args
|
27
|
+
args = {}
|
28
|
+
|
29
|
+
args[:tag] = ":#{@tag.name}" unless self.class::DEFAULT_TAG.nil? || @tag.name == self.class::DEFAULT_TAG
|
30
|
+
|
31
|
+
@tag.attributes.each do |attribute|
|
32
|
+
attr_name = attribute.name
|
33
|
+
|
34
|
+
if self.class::ATTRIBUTES.include?(attr_name)
|
35
|
+
args.merge!(attribute_to_args(attribute))
|
36
|
+
elsif attr_name == "class"
|
37
|
+
args.merge!(map_classes(attribute))
|
38
|
+
else
|
39
|
+
# Assume the attribute is a system argument.
|
40
|
+
args.merge!(SystemArguments.new(attribute).to_args)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
args
|
45
|
+
end
|
46
|
+
|
47
|
+
def attribute_to_args(attribute); end
|
48
|
+
|
49
|
+
def map_classes(classes)
|
50
|
+
system_arguments = system_arguments_to_args(classes.value)
|
51
|
+
args = classes_to_args(system_arguments[:classes])
|
52
|
+
|
53
|
+
args.merge(system_arguments.except(:classes))
|
54
|
+
end
|
55
|
+
|
56
|
+
# Override this with your component's mappings
|
57
|
+
def classes_to_args(classes)
|
58
|
+
raise ConversionError, "Cannot convert classes `#{classes}`" if classes.present?
|
59
|
+
|
60
|
+
{}
|
61
|
+
end
|
62
|
+
|
63
|
+
def system_arguments_to_args(classes)
|
64
|
+
system_arguments = Primer::Classify::Utilities.classes_to_hash(classes)
|
65
|
+
|
66
|
+
# need to transform symbols to strings with leading `:`
|
67
|
+
system_arguments.transform_values do |v|
|
68
|
+
v.is_a?(Symbol) ? ":#{v}" : v
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|