primer_view_components 0.0.46 → 0.0.50
Sign up to get free protection for your applications and to get access to all the features.
- 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
|