primer_view_components 0.0.1 → 0.0.3

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -1
  3. data/README.md +135 -0
  4. data/app/components/primer/avatar_component.rb +28 -0
  5. data/app/components/primer/base_component.rb +42 -0
  6. data/app/components/primer/blankslate_component.html.erb +27 -0
  7. data/app/components/primer/blankslate_component.rb +156 -0
  8. data/app/components/primer/border_box_component.html.erb +26 -0
  9. data/app/components/primer/border_box_component.rb +77 -0
  10. data/app/components/primer/box_component.rb +14 -0
  11. data/app/components/primer/breadcrumb_component.html.erb +8 -0
  12. data/app/components/primer/breadcrumb_component.rb +52 -0
  13. data/app/components/primer/button_component.rb +58 -0
  14. data/app/components/primer/component.rb +9 -0
  15. data/app/components/primer/counter_component.rb +78 -0
  16. data/app/components/primer/details_component.html.erb +6 -0
  17. data/app/components/primer/details_component.rb +38 -0
  18. data/app/components/primer/dropdown_menu_component.html.erb +8 -0
  19. data/app/components/primer/dropdown_menu_component.rb +28 -0
  20. data/app/components/primer/flex_component.rb +81 -0
  21. data/app/components/primer/flex_item_component.rb +21 -0
  22. data/app/components/primer/heading_component.rb +14 -0
  23. data/app/components/primer/label_component.rb +48 -0
  24. data/app/components/primer/layout_component.html.erb +17 -0
  25. data/app/components/primer/layout_component.rb +21 -0
  26. data/app/components/primer/link_component.rb +19 -0
  27. data/app/components/primer/progress_bar_component.html.erb +5 -0
  28. data/app/components/primer/progress_bar_component.rb +66 -0
  29. data/app/components/primer/slot.rb +8 -0
  30. data/app/components/primer/state_component.rb +53 -0
  31. data/app/components/primer/subhead_component.html.erb +17 -0
  32. data/app/components/primer/subhead_component.rb +89 -0
  33. data/app/components/primer/text_component.rb +14 -0
  34. data/app/components/primer/timeline_item_component.html.erb +17 -0
  35. data/app/components/primer/timeline_item_component.rb +69 -0
  36. data/app/components/primer/underline_nav_component.html.erb +11 -0
  37. data/app/components/primer/underline_nav_component.rb +22 -0
  38. data/app/components/primer/view_components.rb +47 -0
  39. data/lib/primer/class_name_helper.rb +27 -0
  40. data/lib/primer/classify.rb +237 -0
  41. data/lib/primer/fetch_or_fallback_helper.rb +41 -0
  42. data/lib/primer/view_components.rb +3 -0
  43. data/lib/primer/view_components/engine.rb +11 -0
  44. data/lib/primer/view_components/version.rb +1 -1
  45. metadata +159 -4
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ class FlexItemComponent < Primer::Component
5
+ FLEX_AUTO_DEFAULT = false
6
+ FLEX_AUTO_ALLOWED_VALUES = [FLEX_AUTO_DEFAULT, true]
7
+
8
+ def initialize(flex_auto: FLEX_AUTO_DEFAULT, **kwargs)
9
+ @kwargs = kwargs
10
+ @kwargs[:classes] =
11
+ class_names(
12
+ @kwargs[:classes],
13
+ "flex-auto" => fetch_or_fallback(FLEX_AUTO_ALLOWED_VALUES, flex_auto, FLEX_AUTO_DEFAULT)
14
+ )
15
+ end
16
+
17
+ def call
18
+ render(Primer::BoxComponent.new(**@kwargs)) { content }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ class HeadingComponent < Primer::Component
5
+ def initialize(**kwargs)
6
+ @kwargs = kwargs
7
+ @kwargs[:tag] ||= :h1
8
+ end
9
+
10
+ def call
11
+ render(Primer::BaseComponent.new(**@kwargs)) { content }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ class LabelComponent < Primer::Component
5
+ SCHEME_MAPPINGS = {
6
+ # gray
7
+ gray: "Label--gray",
8
+ dark_gray: "Label--gray-darker",
9
+
10
+ # colored
11
+ yellow: "Label--yellow",
12
+ orange: "Label--orange",
13
+ red: "Label--red",
14
+ green: "Label--green",
15
+ blue: "Label--blue",
16
+ purple: "Label--purple",
17
+ pink: "Label--pink",
18
+
19
+ # Deprecated
20
+ outline: "Label--outline",
21
+ green_outline: "Label--outline-green",
22
+ }.freeze
23
+ SCHEME_OPTIONS = SCHEME_MAPPINGS.keys << nil
24
+
25
+ VARIANT_MAPPINGS = {
26
+ large: "Label--large",
27
+ inline: "Label--inline",
28
+ }.freeze
29
+ VARIANT_OPTIONS = VARIANT_MAPPINGS.keys << nil
30
+
31
+ def initialize(title:, scheme: nil, variant: nil, **kwargs)
32
+ @kwargs = kwargs
33
+ @kwargs[:bg] = :blue if scheme.nil?
34
+ @kwargs[:tag] ||= :span
35
+ @kwargs[:title] = title
36
+ @kwargs[:classes] = class_names(
37
+ "Label",
38
+ kwargs[:classes],
39
+ SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_OPTIONS, scheme)],
40
+ VARIANT_MAPPINGS[fetch_or_fallback(VARIANT_OPTIONS, variant)]
41
+ )
42
+ end
43
+
44
+ def call
45
+ render(Primer::BaseComponent.new(**@kwargs)) { content }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ <%= render(Primer::FlexComponent.new(**@kwargs)) do %>
2
+ <% if @side == :left %>
3
+ <%= render Primer::BaseComponent.new(tag: :div, classes: "flex-shrink-0", col: (@responsive ? [12, nil, 3] : 3), mb: (@responsive ? [4, nil, 0] : nil)) do %>
4
+ <%= sidebar %>
5
+ <% end %>
6
+ <% end %>
7
+
8
+ <%= render Primer::BaseComponent.new(tag: :div, classes: "flex-shrink-0", col: (@responsive ? [12, nil, 9] : 9), mb: (@responsive ? [4, nil, 0] : nil)) do %>
9
+ <%= main %>
10
+ <% end %>
11
+
12
+ <% if @side == :right %>
13
+ <%= render Primer::BaseComponent.new(tag: :div, classes: "flex-shrink-0", col: (@responsive ? [12, nil, 3] : 3)) do %>
14
+ <%= sidebar %>
15
+ <% end %>
16
+ <% end %>
17
+ <% end %>
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ class LayoutComponent < Primer::Component
5
+ with_content_areas :main, :sidebar
6
+
7
+ DEFAULT_SIDE = :right
8
+ ALLOWED_SIDES = [DEFAULT_SIDE, :left]
9
+
10
+ def initialize(responsive: false, side: DEFAULT_SIDE, **kwargs)
11
+ @kwargs = kwargs
12
+ @side = fetch_or_fallback(ALLOWED_SIDES, side, DEFAULT_SIDE)
13
+ @responsive = responsive
14
+ @kwargs[:classes] = class_names(
15
+ "gutter-condensed gutter-lg",
16
+ @kwargs[:classes]
17
+ )
18
+ @kwargs[:direction] = responsive ? [:column, nil, :row] : nil
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ class LinkComponent < Primer::Component
5
+ def initialize(href:, muted: false, **kwargs)
6
+ @kwargs = kwargs
7
+ @kwargs[:tag] = :a
8
+ @kwargs[:href] = href
9
+ @kwargs[:classes] = class_names(
10
+ @kwargs[:classes],
11
+ "muted-link" => fetch_or_fallback([true, false], muted, false)
12
+ )
13
+ end
14
+
15
+ def call
16
+ render(Primer::BaseComponent.new(**@kwargs)) { content }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ <%= render Primer::BaseComponent.new(**@kwargs) do %>
2
+ <% items.each do |item| %>
3
+ <%= render Primer::BaseComponent.new(**item.kwargs) %>
4
+ <% end %>
5
+ <% end %>
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # Use progress components to visualize task completion.
5
+
6
+ ## Basic example
7
+ #
8
+ # The `Primer::ProgressBarComponent` can take the following arguments:
9
+ #
10
+ # 1. `size` (string). Can be "small" or "large". Increases the height of the progress bar.
11
+ #
12
+ # The `Primer::ProgressBarComponent` uses the [Slots API](https://github.com/github/view_component#slots-experimental) and at least one slot is required for the component to render. Each slot accepts a `percentage` parameter, which is used to set the width of the completed bar.
13
+ #
14
+ # ```ruby
15
+ # <%= render(Primer::ProgressBarComponent.new(size: :small)) do |component| %>
16
+ # <% component.slot(:item, bg: :blue-4, percentage: 50) %>
17
+ # <% end %>
18
+ # ```
19
+ ##
20
+ module Primer
21
+ class ProgressBarComponent < Primer::Component
22
+ include ViewComponent::Slotable
23
+
24
+ with_slot :item, collection: true, class_name: "Item"
25
+
26
+ SIZE_DEFAULT = :default
27
+
28
+ SIZE_MAPPINGS = {
29
+ SIZE_DEFAULT => "",
30
+ :small => "Progress--small",
31
+ :large => "Progress--large",
32
+ }.freeze
33
+
34
+ SIZE_OPTIONS = SIZE_MAPPINGS.keys
35
+
36
+ def initialize(size: SIZE_DEFAULT, percentage: 0, **kwargs)
37
+ @kwargs = kwargs
38
+ @kwargs[:classes] = class_names(
39
+ @kwargs[:classes],
40
+ "Progress",
41
+ SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, SIZE_DEFAULT)]
42
+ )
43
+ @kwargs[:tag] = :span
44
+
45
+ end
46
+
47
+ def render?
48
+ items.any?
49
+ end
50
+
51
+ class Item < ViewComponent::Slot
52
+ include ClassNameHelper
53
+ attr_reader :kwargs
54
+
55
+ def initialize(percentage: 0, bg: :green, **kwargs)
56
+ @percentage = percentage
57
+ @kwargs = kwargs
58
+
59
+ @kwargs[:tag] = :span
60
+ @kwargs[:bg] = bg
61
+ @kwargs[:style] = "width: #{@percentage}%;"
62
+ @kwargs[:classes] = class_names("Progress-item", @kwargs[:classes])
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ class Slot < ViewComponent::Slot
5
+ include ClassNameHelper
6
+ include FetchOrFallbackHelper
7
+ end
8
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ class StateComponent < Primer::Component
5
+ # Component for rendering the status of an item
6
+ #
7
+ # title(string): (required) title attribute
8
+ # color(symbol): label background color
9
+ # size(symbol): label size
10
+ # counter(integer): counter value
11
+ # **args(hash): utility parameters for Primer::Classify
12
+ COLOR_DEFAULT = :default
13
+ COLOR_MAPPINGS = {
14
+ COLOR_DEFAULT => "",
15
+ :green => "State--green",
16
+ :red => "State--red",
17
+ :purple => "State--purple",
18
+ }.freeze
19
+ COLOR_OPTIONS = COLOR_MAPPINGS.keys
20
+
21
+ SIZE_DEFAULT = :default
22
+ SIZE_MAPPINGS = {
23
+ SIZE_DEFAULT => "",
24
+ :small => "State--small",
25
+ }.freeze
26
+ SIZE_OPTIONS = SIZE_MAPPINGS.keys
27
+
28
+ TAG_DEFAULT = :span
29
+ TAG_OPTIONS = [TAG_DEFAULT, :div, :a]
30
+
31
+ def initialize(
32
+ title:,
33
+ color: COLOR_DEFAULT,
34
+ tag: TAG_DEFAULT,
35
+ size: SIZE_DEFAULT,
36
+ **kwargs
37
+ )
38
+ @kwargs = kwargs
39
+ @kwargs[:title] = title
40
+ @kwargs[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, TAG_DEFAULT)
41
+ @kwargs[:classes] = class_names(
42
+ @kwargs[:classes],
43
+ "State",
44
+ COLOR_MAPPINGS[fetch_or_fallback(COLOR_OPTIONS, color, COLOR_DEFAULT)],
45
+ SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, SIZE_DEFAULT)]
46
+ )
47
+ end
48
+
49
+ def call
50
+ render(Primer::BaseComponent.new(**@kwargs)) { content }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,17 @@
1
+ <%= render Primer::BaseComponent.new(**@kwargs) do %>
2
+ <% if heading.present? %>
3
+ <%= render Primer::BaseComponent.new(**heading.kwargs) do %>
4
+ <%= heading.content %>
5
+ <% end %>
6
+ <% end %>
7
+ <% if actions.present? %>
8
+ <%= render Primer::BaseComponent.new(**actions.kwargs) do %>
9
+ <%= actions.content %>
10
+ <% end %>
11
+ <% end %>
12
+ <% if description.present? %>
13
+ <%= render Primer::BaseComponent.new(**description.kwargs) do %>
14
+ <%= description.content %>
15
+ <% end %>
16
+ <% end %>
17
+ <% end %>
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This component consists of a .Subhead container, which has a light gray bottom border.
4
+
5
+ # Use a heading element whenever possible as they can be
6
+ # used as navigation for assistive technologies, and avoid skipping levels.
7
+
8
+ # ## Basic example
9
+
10
+ # The `Primer::SubheadComponent` can take the following arguments:
11
+
12
+ # 1. `heading` (string). The heading to be rendered.
13
+ # 2. `actions` (content). Slot to render any actions to the right of heading.
14
+ # 3. `description` (string). Slot to render description under the heading.
15
+
16
+ # ```erb
17
+ # <%= Primer::SubheadComponent.new(heading: "Hello world")) do |component| %>
18
+ # <% component.slot(:actions) do %>
19
+ # My Actions
20
+ # <% end %>
21
+ # <% end %>
22
+ # ```
23
+ module Primer
24
+ class SubheadComponent < Primer::Component
25
+ include ViewComponent::Slotable
26
+
27
+ with_slot :heading, class_name: "Heading"
28
+ with_slot :actions, class_name: "Actions"
29
+ with_slot :description, class_name: "Description"
30
+
31
+ def initialize(spacious: false, hide_border: false, **kwargs)
32
+ @kwargs = kwargs
33
+
34
+ @kwargs[:tag] = :div
35
+ @kwargs[:classes] =
36
+ class_names(
37
+ @kwargs[:classes],
38
+ "Subhead hx_Subhead--responsive",
39
+ "Subhead--spacious": spacious,
40
+ "border-bottom-0": hide_border
41
+ )
42
+ @kwargs[:mb] ||= hide_border ? 0 : nil
43
+ end
44
+
45
+ def render?
46
+ heading.present?
47
+ end
48
+
49
+ class Heading < ViewComponent::Slot
50
+ include ClassNameHelper
51
+
52
+ attr_reader :kwargs
53
+
54
+ def initialize(danger: false, **kwargs)
55
+ @kwargs = kwargs
56
+ @kwargs[:tag] ||= :div
57
+ @kwargs[:classes] = class_names(
58
+ @kwargs[:classes],
59
+ "Subhead-heading",
60
+ "Subhead-heading--danger": danger
61
+ )
62
+ end
63
+ end
64
+
65
+ class Actions < ViewComponent::Slot
66
+ include ClassNameHelper
67
+
68
+ attr_reader :kwargs
69
+
70
+ def initialize(**kwargs)
71
+ @kwargs = kwargs
72
+ @kwargs[:tag] = :div
73
+ @kwargs[:classes] = class_names(@kwargs[:classes], "Subhead-actions")
74
+ end
75
+ end
76
+
77
+ class Description < ViewComponent::Slot
78
+ include ClassNameHelper
79
+
80
+ attr_reader :kwargs
81
+
82
+ def initialize(**kwargs)
83
+ @kwargs = kwargs
84
+ @kwargs[:tag] = :div
85
+ @kwargs[:classes] = class_names(@kwargs[:classes], "Subhead-description")
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ class TextComponent < Primer::Component
5
+ def initialize(**kwargs)
6
+ @kwargs = kwargs
7
+ @kwargs[:tag] ||= :span
8
+ end
9
+
10
+ def call
11
+ render(Primer::BaseComponent.new(**@kwargs)) { content }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ <%= render Primer::BaseComponent.new(**kwargs) do %>
2
+ <% if avatar %>
3
+ <%= render Primer::AvatarComponent.new(alt: avatar.alt, src: avatar.src, size: avatar.size, square: avatar.square, **avatar.kwargs) %>
4
+ <% end %>
5
+
6
+ <% if badge %>
7
+ <%= render Primer::BaseComponent.new(**badge.kwargs) do %>
8
+ <%= octicon badge.icon %>
9
+ <% end %>
10
+ <% end %>
11
+
12
+ <% if body %>
13
+ <%= render Primer::BaseComponent.new(**body.kwargs) do %>
14
+ <%= body.content %>
15
+ <% end %>
16
+ <% end %>
17
+ <% end %>
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ class TimelineItemComponent < Primer::Component
5
+ include ViewComponent::Slotable
6
+
7
+ with_slot :avatar, class_name: "Avatar"
8
+ with_slot :badge, class_name: "Badge"
9
+ with_slot :body, class_name: "Body"
10
+
11
+ attr_reader :kwargs
12
+ def initialize(condensed: false, **kwargs)
13
+ @kwargs = kwargs
14
+ @kwargs[:tag] = :div
15
+ @kwargs[:classes] = class_names(
16
+ "TimelineItem",
17
+ condensed ? "TimelineItem--condensed" : "",
18
+ kwargs[:classes]
19
+ )
20
+ end
21
+
22
+ def render?
23
+ avatar.present? || badge.present? || body.present?
24
+ end
25
+
26
+ class Avatar < Primer::Slot
27
+ attr_reader :kwargs, :alt, :src, :size, :square
28
+ def initialize(alt: nil, src: nil, size: 40, square: true, **kwargs)
29
+ @alt = alt
30
+ @src = src
31
+ @size = size
32
+ @square = square
33
+
34
+ @kwargs = kwargs
35
+ @kwargs[:tag] = :div
36
+ @kwargs[:classes] = class_names(
37
+ "TimelineItem-avatar",
38
+ kwargs[:classes]
39
+ )
40
+ end
41
+ end
42
+
43
+ class Badge < Primer::Slot
44
+ attr_reader :kwargs, :icon
45
+ def initialize(icon: nil, **kwargs)
46
+ @icon = icon
47
+
48
+ @kwargs = kwargs
49
+ @kwargs[:tag] = :div
50
+ @kwargs[:classes] = class_names(
51
+ "TimelineItem-badge",
52
+ kwargs[:classes]
53
+ )
54
+ end
55
+ end
56
+
57
+ class Body < Primer::Slot
58
+ attr_reader :kwargs
59
+ def initialize(**kwargs)
60
+ @kwargs = kwargs
61
+ @kwargs[:tag] = :div
62
+ @kwargs[:classes] = class_names(
63
+ "TimelineItem-body",
64
+ kwargs[:classes]
65
+ )
66
+ end
67
+ end
68
+ end
69
+ end