primer_view_components 0.0.45 → 0.0.49

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +194 -0
  3. data/app/components/primer/{auto_complete_component.d.ts → auto_complete/auto_component.d.ts} +0 -0
  4. data/app/components/primer/{auto_complete_component.js → auto_complete/auto_component.js} +0 -0
  5. data/app/components/primer/base_component.rb +36 -7
  6. data/app/components/primer/beta/auto_complete.rb +159 -0
  7. data/app/components/primer/beta/auto_complete/auto_complete.d.ts +1 -0
  8. data/app/components/primer/{auto_complete → beta/auto_complete}/auto_complete.html.erb +0 -0
  9. data/app/components/primer/beta/auto_complete/auto_complete.js +1 -0
  10. data/app/components/primer/{auto_complete → beta/auto_complete}/auto_complete.ts +0 -0
  11. data/app/components/primer/beta/auto_complete/item.rb +44 -0
  12. data/app/components/primer/beta/avatar.rb +77 -0
  13. data/app/components/primer/{avatar_stack_component.html.erb → beta/avatar_stack.html.erb} +0 -0
  14. data/app/components/primer/beta/avatar_stack.rb +92 -0
  15. data/app/components/primer/border_box_component.rb +3 -0
  16. data/app/components/primer/component.rb +9 -1
  17. data/app/components/primer/details_component.rb +12 -8
  18. data/app/components/primer/image_crop.html.erb +4 -4
  19. data/app/components/primer/image_crop.rb +1 -1
  20. data/app/components/primer/markdown.rb +9 -9
  21. data/app/components/primer/menu_component.rb +7 -3
  22. data/app/components/primer/navigation/tab_component.rb +34 -6
  23. data/app/components/primer/popover_component.rb +6 -3
  24. data/app/components/primer/primer.d.ts +1 -1
  25. data/app/components/primer/primer.js +1 -1
  26. data/app/components/primer/primer.ts +1 -1
  27. data/app/components/primer/tab_nav_component.rb +9 -6
  28. data/app/components/primer/timeline_item_component.rb +2 -2
  29. data/app/components/primer/tooltip.rb +1 -1
  30. data/app/components/primer/truncate.rb +6 -1
  31. data/app/components/primer/underline_nav_component.rb +13 -6
  32. data/{app/lib → lib}/primer/classify.rb +12 -39
  33. data/{app/lib → lib}/primer/classify/cache.rb +6 -20
  34. data/{app/lib → lib}/primer/classify/flex.rb +0 -0
  35. data/{app/lib → lib}/primer/classify/functional_background_colors.rb +2 -0
  36. data/{app/lib → lib}/primer/classify/functional_border_colors.rb +2 -0
  37. data/{app/lib → lib}/primer/classify/functional_colors.rb +0 -0
  38. data/{app/lib → lib}/primer/classify/functional_text_colors.rb +2 -0
  39. data/{app/lib → lib}/primer/classify/grid.rb +0 -0
  40. data/{app/lib → lib}/primer/classify/utilities.rb +54 -22
  41. data/{app/lib → lib}/primer/classify/utilities.yml +124 -0
  42. data/lib/primer/view_components.rb +35 -6
  43. data/lib/primer/view_components/constants.rb +55 -0
  44. data/lib/primer/view_components/linters/argument_mappers/base.rb +39 -0
  45. data/lib/primer/view_components/linters/argument_mappers/button.rb +35 -44
  46. data/lib/primer/view_components/linters/argument_mappers/clipboard_copy.rb +25 -0
  47. data/lib/primer/view_components/linters/argument_mappers/label.rb +56 -0
  48. data/lib/primer/view_components/linters/argument_mappers/system_arguments.rb +1 -2
  49. data/lib/primer/view_components/linters/autocorrectable.rb +30 -0
  50. data/lib/primer/view_components/linters/button_component_migration_counter.rb +9 -23
  51. data/lib/primer/view_components/linters/clipboard_copy_component_migration_counter.rb +21 -0
  52. data/lib/primer/view_components/linters/helpers.rb +56 -38
  53. data/lib/primer/view_components/linters/label_component_migration_counter.rb +25 -0
  54. data/lib/primer/view_components/statuses.rb +14 -0
  55. data/lib/primer/view_components/version.rb +1 -1
  56. data/lib/rubocop/config/default.yml +12 -0
  57. data/lib/rubocop/cop/primer.rb +4 -0
  58. data/lib/rubocop/cop/primer/no_tag_memoize.rb +42 -0
  59. data/lib/rubocop/cop/primer/system_argument_instead_of_class.rb +75 -0
  60. data/lib/tasks/constants.rake +12 -0
  61. data/lib/tasks/docs.rake +89 -34
  62. data/lib/tasks/utilities.rake +9 -11
  63. data/lib/yard/docs_helper.rb +12 -3
  64. data/static/arguments.yml +977 -0
  65. data/static/assets/view-components.svg +18 -0
  66. data/static/classes.yml +174 -0
  67. data/static/constants.json +628 -0
  68. data/static/statuses.json +5 -5
  69. metadata +44 -27
  70. data/app/components/primer/auto_complete.rb +0 -156
  71. data/app/components/primer/auto_complete/item.rb +0 -42
  72. data/app/components/primer/avatar_component.rb +0 -75
  73. data/app/components/primer/avatar_stack_component.rb +0 -84
  74. data/app/components/primer/details_menu_component.d.ts +0 -1
  75. data/app/components/primer/details_menu_component.js +0 -1
@@ -1,4 +1,71 @@
1
1
  ---
2
+ :animation:
3
+ :fade_in:
4
+ - anim-fade-in
5
+ :fade_out:
6
+ - anim-fade-out
7
+ :fade_up:
8
+ - anim-fade-up
9
+ :fade_down:
10
+ - anim-fade-down
11
+ :grow_x:
12
+ - anim-grow-x
13
+ :shrink_x:
14
+ - anim-shrink-x
15
+ :scale_in:
16
+ - anim-scale-in
17
+ :pulse:
18
+ - anim-pulse
19
+ :pulse_in:
20
+ - anim-pulse-in
21
+ :hover_grow:
22
+ - anim-hover-grow
23
+ :rotate:
24
+ - anim-rotate
25
+ :position:
26
+ :static:
27
+ - position-static
28
+ - position-sm-static
29
+ - position-md-static
30
+ - position-lg-static
31
+ - position-xl-static
32
+ :relative:
33
+ - position-relative
34
+ - position-sm-relative
35
+ - position-md-relative
36
+ - position-lg-relative
37
+ - position-xl-relative
38
+ :absolute:
39
+ - position-absolute
40
+ - position-sm-absolute
41
+ - position-md-absolute
42
+ - position-lg-absolute
43
+ - position-xl-absolute
44
+ :fixed:
45
+ - position-fixed
46
+ - position-sm-fixed
47
+ - position-md-fixed
48
+ - position-lg-fixed
49
+ - position-xl-fixed
50
+ :sticky:
51
+ - position-sticky
52
+ - position-sm-sticky
53
+ - position-md-sticky
54
+ - position-lg-sticky
55
+ - position-xl-sticky
56
+ :vertical_align:
57
+ :middle:
58
+ - v-align-middle
59
+ :top:
60
+ - v-align-top
61
+ :bottom:
62
+ - v-align-bottom
63
+ :text_top:
64
+ - v-align-text-top
65
+ :text_bottom:
66
+ - v-align-text-bottom
67
+ :baseline:
68
+ - v-align-baseline
2
69
  :float:
3
70
  :left:
4
71
  - float-left
@@ -1136,6 +1203,63 @@
1136
1203
  - py-md-12
1137
1204
  - py-lg-12
1138
1205
  - py-xl-12
1206
+ :word_break:
1207
+ :break_all:
1208
+ - wb-break-all
1209
+ :display:
1210
+ :block:
1211
+ - d-block
1212
+ - d-sm-block
1213
+ - d-md-block
1214
+ - d-lg-block
1215
+ - d-xl-block
1216
+ :flex:
1217
+ - d-flex
1218
+ - d-sm-flex
1219
+ - d-md-flex
1220
+ - d-lg-flex
1221
+ - d-xl-flex
1222
+ :inline:
1223
+ - d-inline
1224
+ - d-sm-inline
1225
+ - d-md-inline
1226
+ - d-lg-inline
1227
+ - d-xl-inline
1228
+ :inline_block:
1229
+ - d-inline-block
1230
+ - d-sm-inline-block
1231
+ - d-md-inline-block
1232
+ - d-lg-inline-block
1233
+ - d-xl-inline-block
1234
+ :inline_flex:
1235
+ - d-inline-flex
1236
+ - d-sm-inline-flex
1237
+ - d-md-inline-flex
1238
+ - d-lg-inline-flex
1239
+ - d-xl-inline-flex
1240
+ :none:
1241
+ - d-none
1242
+ - d-sm-none
1243
+ - d-md-none
1244
+ - d-lg-none
1245
+ - d-xl-none
1246
+ :table:
1247
+ - d-table
1248
+ - d-sm-table
1249
+ - d-md-table
1250
+ - d-lg-table
1251
+ - d-xl-table
1252
+ :table_cell:
1253
+ - d-table-cell
1254
+ - d-sm-table-cell
1255
+ - d-md-table-cell
1256
+ - d-lg-table-cell
1257
+ - d-xl-table-cell
1258
+ :visibility:
1259
+ :hidden:
1260
+ - v-hidden
1261
+ :visible:
1262
+ - v-visible
1139
1263
  :hide:
1140
1264
  :sm:
1141
1265
  - hide-sm
@@ -1,27 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "primer/classify"
3
4
  require "primer/view_components/version"
4
5
  require "primer/view_components/engine"
5
6
 
6
7
  module Primer
7
8
  # :nodoc:
8
9
  module ViewComponents
9
- DEFAULT_STATUSES_PATH = File.expand_path("static")
10
+ DEFAULT_STATIC_PATH = File.expand_path("static")
10
11
  DEFAULT_STATUS_FILE_NAME = "statuses.json"
12
+ DEFAULT_CONSTANTS_FILE_NAME = "constants.json"
11
13
 
12
14
  # generate_statuses returns a hash mapping component name to
13
15
  # the component's status sorted alphabetically by the component name.
14
16
  def self.generate_statuses
15
- statuses = Primer::Component.descendants.each_with_object({}) do |component, mem|
17
+ Primer::Component.descendants.sort_by(&:name).each_with_object({}) do |component, mem|
16
18
  mem[component.to_s] = component.status.to_s
17
19
  end
18
-
19
- statuses.sort_by { |k, _v| k }.to_h
20
20
  end
21
21
 
22
22
  # dump_statuses generates the status hash and then serializes
23
23
  # it as json at the given path
24
- def self.dump_statuses(path: DEFAULT_STATUSES_PATH)
24
+ def self.dump_statuses(path: DEFAULT_STATIC_PATH)
25
25
  require "json"
26
26
 
27
27
  statuses = generate_statuses
@@ -34,8 +34,37 @@ module Primer
34
34
 
35
35
  # read_statuses returns a JSON string matching the output of
36
36
  # generate_statuses
37
- def self.read_statuses(path: DEFAULT_STATUSES_PATH)
37
+ def self.read_statuses(path: DEFAULT_STATIC_PATH)
38
38
  File.read(File.join(path, DEFAULT_STATUS_FILE_NAME))
39
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
40
69
  end
41
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,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "conversion_error"
4
+ require_relative "system_arguments"
5
+ require "primer/view_components/constants"
6
+
7
+ module ERBLint
8
+ module Linters
9
+ module ArgumentMappers
10
+ # Provides the base interface to implement an `ArgumentMapper`.
11
+ # Override attribute_to_args in a child class to customize its mapping behavior.
12
+ class Base
13
+ DEFAULT_TAG = nil
14
+
15
+ def initialize(tag)
16
+ @tag = tag
17
+ end
18
+
19
+ def to_s
20
+ to_args.map { |k, v| "#{k}: #{v}" }.join(", ")
21
+ end
22
+
23
+ def to_args
24
+ args = {}
25
+
26
+ args[:tag] = ":#{@tag.name}" unless self.class::DEFAULT_TAG.nil? || @tag.name == self.class::DEFAULT_TAG
27
+
28
+ @tag.attributes.each do |attribute|
29
+ args.merge!(attribute_to_args(attribute))
30
+ end
31
+
32
+ args
33
+ end
34
+
35
+ def attribute_to_args(attribute); end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,62 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "conversion_error"
4
- require_relative "system_arguments"
3
+ require_relative "base"
5
4
 
6
5
  module ERBLint
7
6
  module Linters
8
7
  module ArgumentMappers
9
8
  # Maps classes in a button element to arguments for the Button component.
10
- class Button
11
- SCHEME_MAPPINGS = {
12
- "btn-primary" => ":primary",
13
- "btn-danger" => ":danger",
14
- "btn-outline" => ":outline",
15
- "btn-invisible" => ":invisible",
16
- "btn-link" => ":link"
17
- }.freeze
9
+ class Button < Base
10
+ require "pry"
18
11
 
19
- VARIANT_MAPPINGS = {
20
- "btn-sm" => ":small",
21
- "btn-large" => ":large"
22
- }.freeze
12
+ SCHEME_MAPPINGS = Primer::ViewComponents::Constants.get(
13
+ component: "Primer::ButtonComponent",
14
+ constant: "SCHEME_MAPPINGS",
15
+ symbolize: true
16
+ ).freeze
23
17
 
24
- TYPE_OPTIONS = %w[button reset submit].freeze
18
+ VARIANT_MAPPINGS = Primer::ViewComponents::Constants.get(
19
+ component: "Primer::ButtonComponent",
20
+ constant: "VARIANT_MAPPINGS",
21
+ symbolize: true
22
+ ).freeze
25
23
 
26
- def initialize(tag)
27
- @tag = tag
28
- end
29
-
30
- def to_s
31
- to_args.map { |k, v| "#{k}: #{v}" }.join(", ")
32
- end
33
-
34
- def to_args
35
- args = {}
24
+ TYPE_OPTIONS = Primer::ViewComponents::Constants.get(
25
+ component: "Primer::BaseButton",
26
+ constant: "TYPE_OPTIONS"
27
+ ).freeze
28
+ DEFAULT_TAG = Primer::ViewComponents::Constants.get(
29
+ component: "Primer::BaseButton",
30
+ constant: "DEFAULT_TAG"
31
+ ).freeze
36
32
 
37
- args[:tag] = ":#{@tag.name}" unless @tag.name == "button"
33
+ def attribute_to_args(attribute)
34
+ attr_name = attribute.name
38
35
 
39
- @tag.attributes.each do |attribute|
40
- attr_name = attribute.name
36
+ if attr_name == "class"
37
+ classes_to_args(attribute)
38
+ elsif attr_name == "disabled"
39
+ { disabled: true }
40
+ elsif attr_name == "type"
41
+ # button is the default type, so we don't need to do anything.
42
+ return {} if attribute.value == "button"
41
43
 
42
- if attr_name == "class"
43
- args = args.merge(classes_to_args(attribute))
44
- elsif attr_name == "disabled"
45
- args[:disabled] = true
46
- elsif attr_name == "type"
47
- # button is the default type, so we don't need to do anything.
48
- next if attribute.value == "button"
44
+ raise ConversionError, "Button component does not support type \"#{attribute.value}\"" unless TYPE_OPTIONS.include?(attribute.value)
49
45
 
50
- raise ConversionError, "Button component does not support type \"#{attribute.value}\"" unless TYPE_OPTIONS.include?(attribute.value)
51
-
52
- args[:type] = ":#{attribute.value}"
53
- else
54
- # Assume the attribute is a system argument.
55
- args.merge!(SystemArguments.new(attribute).to_args)
56
- end
46
+ { type: ":#{attribute.value}" }
47
+ else
48
+ # Assume the attribute is a system argument.
49
+ SystemArguments.new(attribute).to_args
57
50
  end
58
-
59
- args
60
51
  end
61
52
 
62
53
  def classes_to_args(classes)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module ArgumentMappers
8
+ # Maps attributes in the clipboard-copy element to arguments for the ClipboardCopy component.
9
+ class ClipboardCopy < Base
10
+ DEFAULT_TAG = "clipboard-copy"
11
+
12
+ def attribute_to_args(attribute)
13
+ attr_name = attribute.name
14
+
15
+ if attr_name == "value"
16
+ { value: attribute.value.to_json }
17
+ else
18
+ # Assume the attribute is a system argument.
19
+ SystemArguments.new(attribute).to_args
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ERBLint
6
+ module Linters
7
+ module ArgumentMappers
8
+ # Maps classes in a label element to arguments for the Label component.
9
+ class Label < Base
10
+ SCHEME_MAPPINGS = Primer::ViewComponents::Constants.get(
11
+ component: "Primer::LabelComponent",
12
+ constant: "SCHEME_MAPPINGS",
13
+ symbolize: true
14
+ ).freeze
15
+
16
+ VARIANT_MAPPINGS = Primer::ViewComponents::Constants.get(
17
+ component: "Primer::LabelComponent",
18
+ constant: "VARIANT_MAPPINGS",
19
+ symbolize: true
20
+ ).freeze
21
+
22
+ DEFAULT_TAG = Primer::ViewComponents::Constants.get(
23
+ component: "Primer::LabelComponent",
24
+ constant: "DEFAULT_TAG"
25
+ ).freeze
26
+
27
+ def attribute_to_args(attribute)
28
+ attr_name = attribute.name
29
+
30
+ if attr_name == "class"
31
+ classes_to_args(attribute)
32
+ elsif attr_name == "title"
33
+ { title: attribute.value.to_json }
34
+ else
35
+ # Assume the attribute is a system argument.
36
+ SystemArguments.new(attribute).to_args
37
+ end
38
+ end
39
+
40
+ def classes_to_args(classes)
41
+ classes.value.split(" ").each_with_object({}) do |class_name, acc|
42
+ next if class_name == "Label"
43
+
44
+ if SCHEME_MAPPINGS[class_name] && acc[:scheme].nil?
45
+ acc[:scheme] = SCHEME_MAPPINGS[class_name]
46
+ elsif VARIANT_MAPPINGS[class_name] && acc[:variant].nil?
47
+ acc[:variant] = VARIANT_MAPPINGS[class_name]
48
+ else
49
+ raise ConversionError, "Cannot convert class \"#{class_name}\""
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end