primer_view_components 0.0.96 → 0.0.97

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/app/assets/javascripts/primer_view_components.js +1 -1
  4. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  5. data/app/assets/styles/primer_view_components.css +1 -1
  6. data/app/assets/styles/primer_view_components.css.map +1 -1
  7. data/app/components/primer/alpha/action_list/action-list-selection.pcss +92 -0
  8. data/app/components/primer/alpha/action_list/action-list.pcss +620 -0
  9. data/app/components/primer/alpha/action_list/divider.rb +35 -0
  10. data/app/components/primer/alpha/action_list/heading.html.erb +8 -0
  11. data/app/components/primer/alpha/action_list/heading.rb +38 -0
  12. data/app/components/primer/alpha/action_list/item.html.erb +39 -0
  13. data/app/components/primer/alpha/action_list/item.rb +230 -0
  14. data/app/components/primer/alpha/action_list.html.erb +15 -0
  15. data/app/components/primer/alpha/action_list.rb +112 -0
  16. data/app/components/primer/alpha/dialog/header.rb +1 -1
  17. data/app/components/primer/alpha/nav_list/item.html.erb +13 -0
  18. data/app/components/primer/alpha/nav_list/item.rb +89 -0
  19. data/app/components/primer/alpha/nav_list/section.html.erb +3 -0
  20. data/app/components/primer/alpha/nav_list/section.rb +88 -0
  21. data/app/components/primer/alpha/nav_list.d.ts +25 -0
  22. data/app/components/primer/alpha/nav_list.html.erb +10 -0
  23. data/app/components/primer/alpha/nav_list.js +130 -0
  24. data/app/components/primer/alpha/nav_list.rb +112 -0
  25. data/app/components/primer/alpha/nav_list.ts +129 -0
  26. data/app/components/primer/primer.d.ts +1 -0
  27. data/app/components/primer/primer.js +1 -0
  28. data/app/components/primer/primer.pcss +1 -0
  29. data/app/components/primer/primer.ts +1 -0
  30. data/lib/postcss_mixins/activeIndicatorLine.pcss +11 -0
  31. data/lib/primer/view_components/version.rb +1 -1
  32. data/lib/tasks/docs.rake +51 -22
  33. data/lib/yard/docs_helper.rb +3 -3
  34. data/static/arguments.json +267 -3
  35. data/static/audited_at.json +7 -0
  36. data/static/constants.json +76 -0
  37. data/static/statuses.json +7 -0
  38. metadata +21 -4
  39. data/app/components/primer/experimental/action_bar.d.ts +0 -14
  40. data/app/components/primer/experimental/action_bar.js +0 -141
@@ -0,0 +1,39 @@
1
+ <%= render(Primer::BaseComponent.new(tag: :li, **@system_arguments)) do %>
2
+ <%= render(Primer::BaseComponent.new(**@content_arguments)) do %>
3
+ <% if private_leading_action_icon %>
4
+ <span class="ActionListItem-visual ActionListItem-action--leading">
5
+ <%= private_leading_action_icon %>
6
+ </span>
7
+ <% end %>
8
+ <% if leading_visual %>
9
+ <span class="ActionListItem-visual ActionListItem-visual--leading">
10
+ <%= leading_visual %>
11
+ </span>
12
+ <% end %>
13
+ <%= render(Primer::ConditionalWrapper.new(condition: description?, tag: :span, **@description_wrapper_arguments)) do %>
14
+ <%= render(Primer::BaseComponent.new(tag: :span, **@label_arguments)) do %>
15
+ <%= @label %>
16
+ <% end %>
17
+ <% if description? %>
18
+ <span class="ActionListItem-description">
19
+ <%= description %>
20
+ </span>
21
+ <% end %>
22
+ <% end %>
23
+ <% if trailing_visual %>
24
+ <span class="ActionListItem-visual ActionListItem-visual--trailing">
25
+ <%= trailing_visual %>
26
+ </span>
27
+ <% end %>
28
+ <% if private_trailing_action_icon %>
29
+ <span class="ActionListItem-visual ActionListItem-action--trailing">
30
+ <%= private_trailing_action_icon %>
31
+ </span>
32
+ <% end %>
33
+ <% end %>
34
+ <%= tooltip %>
35
+ <% if trailing_action %>
36
+ <%= trailing_action %>
37
+ <% end %>
38
+ <%= private_content %>
39
+ <% end %>
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Alpha
5
+ class ActionList
6
+ # An individual `ActionList` item. Items can optionally include leading and/or trailing visuals,
7
+ # such as icons, avatars, and counters.
8
+ class Item < Primer::Component
9
+ DEFAULT_SIZE = :medium
10
+ SIZE_MAPPINGS = {
11
+ DEFAULT_SIZE => nil,
12
+ :large => "ActionListContent--sizeLarge",
13
+ :xlarge => "ActionListContent--sizeXLarge"
14
+ }.freeze
15
+ SIZE_OPTIONS = SIZE_MAPPINGS.keys.freeze
16
+
17
+ DEFAULT_DESCRIPTION_SCHEME = :block
18
+ DESCRIPTION_SCHEME_MAPPINGS = {
19
+ :inline => "ActionListItem-descriptionWrap--inline",
20
+ DEFAULT_DESCRIPTION_SCHEME => "ActionListItem-descriptionWrap"
21
+ }.freeze
22
+ DESCRIPTION_SCHEME_OPTIONS = DESCRIPTION_SCHEME_MAPPINGS.keys.freeze
23
+
24
+ DEFAULT_SCHEME = :default
25
+ SCHEME_MAPPINGS = {
26
+ DEFAULT_SCHEME => nil,
27
+ :danger => "ActionListItem--danger"
28
+ }.freeze
29
+ SCHEME_OPTIONS = SCHEME_MAPPINGS.keys.freeze
30
+
31
+ # Description content that complements the item's label. See `ActionList`'s `description_scheme` argument
32
+ # for layout options.
33
+ renders_one :description
34
+
35
+ # An icon or avatar that will render to the left of the label.
36
+ #
37
+ # To render an icon, call the `with_leading_visual_icon` method, which accepts the arguments accepted by <%= link_to_component(Primer::OcticonComponent) %>.
38
+ #
39
+ # To render an avatar, call the `with_leading_visual_avatar` method, which accepts the arguments accepted by <%= link_to_component(Primer::Beta::Avatar) %>.
40
+ renders_one :leading_visual, types: {
41
+ icon: Primer::OcticonComponent,
42
+ avatar: ->(**kwargs) { Primer::Beta::Avatar.new(**{ **kwargs, size: 16 }) }
43
+ }
44
+
45
+ # Used internally.
46
+ #
47
+ # @private
48
+ renders_one :private_leading_action_icon, Primer::OcticonComponent
49
+
50
+ # An icon, label, counter, or text to render to the right of the label.
51
+ #
52
+ # To render an icon, call the `with_leading_visual_icon` method, which accepts the arguments accepted by <%= link_to_component(Primer::OcticonComponent) %>.
53
+ #
54
+ # To render a label, call the `with_leading_visual_label` method, which accepts the arguments accepted by <%= link_to_component(Primer::LabelComponent) %>.
55
+ #
56
+ # To render a counter, call the `with_leading_visual_counter` method, which accepts the arguments accepted by <%= link_to_component(Primer::CounterComponent) %>.
57
+ #
58
+ # To render text, call the `with_leading_visual_text` method and pass a block that returns a string. Eg:
59
+ # ```ruby
60
+ # with_leading_visual_text { "Text here" }`
61
+ # ```
62
+ renders_one :trailing_visual, types: {
63
+ icon: Primer::OcticonComponent,
64
+ label: Primer::LabelComponent,
65
+ counter: Primer::CounterComponent,
66
+ text: ->(text) { text }
67
+ }
68
+
69
+ # Used internally.
70
+ #
71
+ # @private
72
+ renders_one :private_trailing_action_icon, Primer::OcticonComponent
73
+
74
+ # A button rendered after the trailing icon that can be used to show a menu, activate
75
+ # a dialog, etc.
76
+ #
77
+ # @param show_on_hover [Boolean] Whether or not to show the button when the list item is hovered. If `true`, the button will be invisible until hovered. If `false`, the button will always be visible. Defaults to `false`.
78
+ # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Beta::IconButton) %>.
79
+ renders_one :trailing_action, lambda { |show_on_hover: false, **system_arguments|
80
+ @trailing_action_on_hover = show_on_hover
81
+
82
+ Primer::Beta::IconButton.new(scheme: :invisible, classes: ["ActionListItem-trailingAction"], **system_arguments)
83
+ }
84
+
85
+ # `Tooltip` that appears on mouse hover or keyboard focus over the trailing action button. Use tooltips sparingly and as
86
+ # a last resort. **Important:** This tooltip defaults to `type: :description`. In a few scenarios, `type: :label` may be
87
+ # more appropriate. Consult the <%= link_to_component(Primer::Alpha::Tooltip) %> documentation for more information.
88
+ #
89
+ # @param type [Symbol] (:description) <%= one_of(Primer::Alpha::Tooltip::TYPE_OPTIONS) %>
90
+ # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::Tooltip) %>.
91
+ renders_one :tooltip, lambda { |**system_arguments|
92
+ raise ArgumentError, "Buttons with a tooltip must have a unique `id` set on the `Button`." if @id.blank? && !Rails.env.production?
93
+
94
+ system_arguments[:for_id] = @id
95
+ system_arguments[:type] ||= :description
96
+
97
+ Primer::Alpha::Tooltip.new(**system_arguments)
98
+ }
99
+
100
+ # Used internally.
101
+ #
102
+ # @private
103
+ renders_one :private_content
104
+
105
+ attr_reader :list, :active, :disabled, :parent
106
+
107
+ # Whether or not this item is active.
108
+ #
109
+ # @return [Boolean]
110
+ alias active? active
111
+
112
+ # Whether or not this item is disabled.
113
+ #
114
+ # @return [Boolean]
115
+ alias disabled? disabled
116
+
117
+ # @param list [Primer::Alpha::ActionList] The list that contains this item. Used internally.
118
+ # @param parent [Primer::Alpha::ActionList::Item] This item's parent item. `nil` if this item is at the root. Used internally.
119
+ # @param label [String] Item label.
120
+ # @param label_classes [String] CSS classes that will be added to the label.
121
+ # @param truncate_label [Boolean] Truncate label with ellipsis.
122
+ # @param href [String] Link URL.
123
+ # @param role [String] ARIA role describing the function of the item.
124
+ # @param size [Symbol] Controls block sizing of the item.
125
+ # @param scheme [Symbol] Controls color/style based on behavior.
126
+ # @param disabled [Boolean] Disabled items are not clickable and visually dim.
127
+ # @param description_scheme [Symbol] Display description inline with label, or block on the next line.
128
+ # @param active [Boolean] Sets an active state on navigational items.
129
+ # @param on_click [String] JavaScript to execute when the item is clicked.
130
+ # @param id [String] Used internally.
131
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
132
+ def initialize(
133
+ list:,
134
+ label:,
135
+ label_classes: nil,
136
+ parent: nil,
137
+ truncate_label: false,
138
+ href: nil,
139
+ role: :listitem,
140
+ size: DEFAULT_SIZE,
141
+ scheme: DEFAULT_SCHEME,
142
+ disabled: false,
143
+ description_scheme: DEFAULT_DESCRIPTION_SCHEME,
144
+ active: false,
145
+ on_click: nil,
146
+ id: SecureRandom.hex,
147
+ **system_arguments
148
+ )
149
+ @list = list
150
+ @parent = parent
151
+ @label = label
152
+ @href = href
153
+ @truncate_label = truncate_label
154
+ @disabled = disabled
155
+ @active = active
156
+ @trailing_action_on_hover = false
157
+ @id = id
158
+ @system_arguments = system_arguments
159
+
160
+ @size = fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)
161
+ @scheme = fetch_or_fallback(SCHEME_OPTIONS, scheme, DEFAULT_SCHEME)
162
+ @description_scheme = fetch_or_fallback(
163
+ DESCRIPTION_SCHEME_OPTIONS, description_scheme, DEFAULT_DESCRIPTION_SCHEME
164
+ )
165
+
166
+ @system_arguments[:classes] = class_names(
167
+ @system_arguments[:classes],
168
+ SCHEME_MAPPINGS[@scheme],
169
+ "ActionListItem",
170
+ "ActionListItem--navActive" => @active
171
+ )
172
+
173
+ @system_arguments[:role] = role
174
+
175
+ @system_arguments[:aria] ||= {}
176
+ @system_arguments[:aria][:disabled] = "true" if @disabled
177
+
178
+ @label_arguments = {
179
+ classes: class_names(
180
+ label_classes,
181
+ "ActionListItem-label",
182
+ "ActionListItem-label--truncate" => @truncate_label
183
+ )
184
+ }
185
+
186
+ @content_arguments = {
187
+ id: @id,
188
+ classes: class_names(
189
+ "ActionListContent",
190
+ SIZE_MAPPINGS[@size]
191
+ )
192
+ }
193
+
194
+ if @href && !@disabled
195
+ @content_arguments[:tag] = :a
196
+ @content_arguments[:href] = @href
197
+ else
198
+ @content_arguments[:tag] = :span
199
+ @content_arguments[:onclick] = on_click if on_click
200
+ end
201
+
202
+ @description_wrapper_arguments = {
203
+ classes: class_names(
204
+ "ActionListItem-descriptionWrap",
205
+ DESCRIPTION_SCHEME_MAPPINGS[@description_scheme]
206
+ )
207
+ }
208
+ end
209
+
210
+ private
211
+
212
+ def before_render
213
+ @system_arguments[:classes] = class_names(
214
+ @system_arguments[:classes],
215
+ "ActionListItem--withActions" => trailing_action.present?,
216
+ "ActionListItem--trailingActionHover" => @trailing_action_on_hover
217
+ )
218
+
219
+ return unless leading_visual
220
+
221
+ @content_arguments[:classes] = class_names(
222
+ @content_arguments[:classes],
223
+ "ActionListContent--visual16" => leading_visual,
224
+ "ActionListContent--blockDescription" => description && @description_scheme == :block
225
+ )
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,15 @@
1
+ <%= render(Primer::BaseComponent.new(tag: :ul, role: @role, classes: "ActionListWrap")) do %>
2
+ <% if heading %>
3
+ <%= heading %>
4
+ <% end %>
5
+ <%= render(Primer::BaseComponent.new(tag: :li, **@list_wrapper_arguments)) do %>
6
+ <%= render(Primer::BaseComponent.new(tag: :ul, **@system_arguments)) do %>
7
+ <% items.each_with_index do |item, index| %>
8
+ <% if index > 0 && @show_dividers %>
9
+ <%= render(Primer::Alpha::ActionList::Divider.new) %>
10
+ <% end %>
11
+ <%= item %>
12
+ <% end %>
13
+ <% end %>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Alpha
5
+ # An ActionList is a styled list of links. It acts as the base component for many
6
+ # other menu-type components, including `ActionMenu` and `SelectPanel`, as well as
7
+ # the navigational component `NavList`.
8
+ #
9
+ # Each item in an action list can be augmented by specifying corresponding leading
10
+ # and/or trailing visuals.
11
+ class ActionList < Primer::Component
12
+ status :alpha
13
+
14
+ DEFAULT_ROLE = :list
15
+
16
+ DEFAULT_SCHEME = :full
17
+ SCHEME_MAPPINGS = {
18
+ DEFAULT_SCHEME => nil,
19
+ :inset => "ActionListWrap--inset"
20
+ }.freeze
21
+ SCHEME_OPTIONS = SCHEME_MAPPINGS.keys.freeze
22
+
23
+ # @private
24
+ def self.custom_element_name
25
+ @custom_element_name ||= name.split("::").last.underscore.dasherize
26
+ end
27
+
28
+ # @private
29
+ def custom_element_name
30
+ self.class.custom_element_name
31
+ end
32
+
33
+ # Heading text rendered above the list of items.
34
+ #
35
+ # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Heading) %>.
36
+ renders_one :heading, lambda { |**system_arguments|
37
+ Heading.new(list_id: @id, **system_arguments)
38
+ }
39
+
40
+ # Items.
41
+ #
42
+ # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Item) %>.
43
+ renders_many :items, lambda { |**system_arguments|
44
+ build_item(**system_arguments).tap do |item|
45
+ will_add_item(item)
46
+ end
47
+ }
48
+
49
+ # @param role [Boolean] ARIA role describing the function of the list. listbox and menu are a common values.
50
+ # @param item_classes [String] Additional CSS classes to attach to items.
51
+ # @param scheme [Symbol] <%= one_of(Primer::Alpha::ActionList::SCHEME_OPTIONS) %>. `inset` children are offset (vertically and horizontally) from list edges. `full` (default) children are flush (vertically and horizontally) with list edges.
52
+ # @param show_dividers [Boolean] Display a divider above each item in the list when it does not follow a header or divider.
53
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
54
+ def initialize(
55
+ role: DEFAULT_ROLE,
56
+ item_classes: nil,
57
+ scheme: DEFAULT_SCHEME,
58
+ show_dividers: false,
59
+ **system_arguments
60
+ )
61
+ @id = "action-list-#{SecureRandom.uuid}"
62
+ @role = role
63
+
64
+ @system_arguments = system_arguments
65
+ @system_arguments[:tag] = :ul
66
+ @item_classes = item_classes
67
+ @scheme = fetch_or_fallback(SCHEME_OPTIONS, scheme, DEFAULT_SCHEME)
68
+ @show_dividers = show_dividers
69
+ @system_arguments[:classes] = class_names(
70
+ SCHEME_MAPPINGS[@scheme],
71
+ system_arguments[:classes],
72
+ "ActionListWrap",
73
+ "ActionListWrap--subGroup",
74
+ "ActionListWrap--divided" => @show_dividers
75
+ )
76
+
77
+ @list_wrapper_arguments = {}
78
+ end
79
+
80
+ # @private
81
+ def before_render
82
+ return if @sub_group
83
+
84
+ if heading.present?
85
+ @system_arguments[:"aria-labelledby"] = @id
86
+ elsif aria(:label, @system_arguments).blank?
87
+ raise ArgumentError, "An aria-label or heading must be provided"
88
+ end
89
+
90
+ return if items.blank?
91
+
92
+ @list_wrapper_arguments[:classes] = class_names(
93
+ @list_wrapper_arguments[:classes],
94
+ "ActionListItem--hasSubItem"
95
+ )
96
+ end
97
+
98
+ # @private
99
+ def build_item(**system_arguments)
100
+ system_arguments[:classes] = class_names(
101
+ @item_classes,
102
+ system_arguments[:classes]
103
+ )
104
+
105
+ ActionList::Item.new(list: self, **system_arguments)
106
+ end
107
+
108
+ # @private
109
+ def will_add_item(_item); end
110
+ end
111
+ end
112
+ end
@@ -24,7 +24,7 @@ module Primer
24
24
  @subtitle = subtitle
25
25
  @visually_hide_title = visually_hide_title
26
26
  @system_arguments = deny_tag_argument(**system_arguments)
27
- @system_arguments[:tag] = :header
27
+ @system_arguments[:tag] = :div
28
28
  @system_arguments[:classes] = class_names(
29
29
  "Overlay-header",
30
30
  { "Overlay-header--divided": show_divider },
@@ -0,0 +1,13 @@
1
+ <% with_private_content do %>
2
+ <% unless items.empty? %>
3
+ <% capture do %>
4
+ <%= render(Primer::BaseComponent.new(tag: :ul, **@sub_list_arguments)) do %>
5
+ <% items.each do |item| %>
6
+ <%= item %>
7
+ <% end %>
8
+ <% end %>
9
+ <% end %>
10
+ <% end %>
11
+ <% end %>
12
+
13
+ <%= render_parent %>
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Alpha
5
+ class NavList
6
+ # Items are rendered as styled links. They can optionally include leading and/or trailing visuals,
7
+ # such as icons, avatars, and counters. Items are selected by ID. IDs can be specified via the
8
+ # `selected_item_ids` argument, which accepts a list of valid IDs for the item. Items can also
9
+ # themselves contain sub items. Sub items are rendered collapsed by default.
10
+ class Item < Primer::Alpha::ActionList::Item
11
+ attr_reader :selected_by_ids, :sub_item
12
+
13
+ # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Item) %>.
14
+ renders_many :items, lambda { |**system_arguments|
15
+ raise "Items can only be nested 2 levels deep" if sub_item?
16
+
17
+ @list.build_item(parent: self, sub_item: true, **system_arguments).tap do |item|
18
+ @list.will_add_item(item)
19
+
20
+ if item.active?
21
+ @content_arguments[:classes] = class_names(
22
+ @content_arguments[:classes],
23
+ "ActionListContent--hasActiveSubItem"
24
+ )
25
+ end
26
+ end
27
+ }
28
+
29
+ # Whether or not this item is nested under a parent item.
30
+ #
31
+ # @return [Boolean]
32
+ alias sub_item? sub_item
33
+
34
+ # @param selected_item_id [Symbol] The ID of the currently selected list item. Used internally.
35
+ # @param selected_by_ids [Array<Symbol>] The list of IDs that select this item. In other words, if the `selected_item_id` attribute on the parent `NavList` is set to one of these IDs, the item will appear selected.
36
+ # @param expanded [Boolean] Whether this item shows (expands) or hides (collapses) its list of sub items.
37
+ # @param sub_item [Boolean] Whether or not this item is nested under a parent item. Used internally.
38
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
39
+ def initialize(selected_item_id: nil, selected_by_ids: [], sub_item: false, expanded: false, **system_arguments)
40
+ @selected_item_id = selected_item_id
41
+ @selected_by_ids = Array(selected_by_ids)
42
+ @expanded = expanded
43
+ @sub_item = sub_item
44
+
45
+ system_arguments[:classes] = class_names(
46
+ system_arguments[:classes],
47
+ "ActionListItem--subItem" => @sub_item
48
+ )
49
+
50
+ @sub_list_arguments = {
51
+ classes: class_names(
52
+ "ActionList",
53
+ "ActionList--subGroup"
54
+ )
55
+ }
56
+
57
+ overrides = { "data-item-id": @selected_by_ids.join(" ") }
58
+ overrides[:active] = @selected_by_ids.include?(@selected_item_id)
59
+
60
+ super(**system_arguments, **overrides)
61
+ end
62
+
63
+ # Cause this item to show its list of sub items when rendered.
64
+ def expand!
65
+ @expanded = true
66
+ end
67
+
68
+ def before_render
69
+ super
70
+
71
+ raise "Cannot render a trailing visual for an item with subitems" if items.present? && trailing_visual.present?
72
+
73
+ return if items.blank?
74
+
75
+ @content_arguments[:tag] = :button
76
+ @content_arguments[:"aria-expanded"] = @expanded.to_s
77
+ @content_arguments[:"data-action"] = "click:#{@list.custom_element_name}#handleItemWithSubItemClick"
78
+
79
+ with_private_trailing_action_icon(:"chevron-down", classes: "ActionListItem-collapseIcon")
80
+
81
+ @system_arguments[:classes] = class_names(
82
+ @system_arguments[:classes],
83
+ "ActionListItem--hasSubItem"
84
+ )
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ <nav-list>
2
+ <%= render_parent %>
3
+ </nav-list>
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Alpha
5
+ class NavList
6
+ # A logical grouping of navigation links with an optional heading.
7
+ #
8
+ # See <%= link_to_component(Primer::Alpha::NavList) %> for usage examples.
9
+ class Section < ActionList
10
+ # A special "show more" list item that appears at the bottom of the section. Clicking
11
+ # the item will fetch the next page of results from the URL passed in the `src` argument
12
+ # and append the resulting chunk of HTML to the section.
13
+ #
14
+ # @param src [String] The URL to query for additional pages of list items.
15
+ # @param pages [Integer] The total number of pages in the result set.
16
+ # @param component_klass [Class] A component class to use instead of the default `Primer::Alpha::NavList::Item` class.
17
+ # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::NavList::Item) %>.
18
+ renders_one :show_more_item, lambda { |src:, pages:, component_klass: NavList::Item, **system_arguments|
19
+ system_arguments[:classes] = class_names(
20
+ @item_classes,
21
+ system_arguments[:classes]
22
+ )
23
+ system_arguments[:id] = "ActionList--showMoreItem"
24
+ system_arguments[:hidden] = true
25
+ system_arguments[:href] = "#"
26
+ system_arguments[:data] ||= {}
27
+ system_arguments[:data][:target] = "nav-list.showMoreItem"
28
+ system_arguments[:data][:action] = "click:nav-list#showMore"
29
+ system_arguments[:data][:"current-page"] = "1"
30
+ system_arguments[:data][:"total-pages"] = pages.to_s
31
+ system_arguments[:label_classes] = class_names(
32
+ system_arguments[:label_classes],
33
+ "color-fg-accent"
34
+ )
35
+
36
+ component_klass.new(list: self, src: src, **system_arguments)
37
+ }
38
+
39
+ # @private
40
+ def self.custom_element_name
41
+ "nav-list"
42
+ end
43
+
44
+ # @param selected_item_id [Symbol] The ID of the currently selected item. Used internally.
45
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
46
+ def initialize(selected_item_id: nil, **system_arguments)
47
+ @system_arguments = system_arguments
48
+ @selected_item_id = selected_item_id
49
+ @system_arguments[:"data-target"] = "nav-list.list"
50
+
51
+ super(**@system_arguments)
52
+ end
53
+
54
+ # Cause this section to show its list of sub items when rendered.
55
+ def expand!
56
+ @expanded = true
57
+ end
58
+
59
+ # The items contained within this section.
60
+ #
61
+ # @return [Array<Primer::Alpha::ActionList::Item>]
62
+ def items
63
+ [*super, show_more_item].tap(&:compact!)
64
+ end
65
+
66
+ # @!parse
67
+ # # Items.
68
+ # #
69
+ # # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::NavList::Item) %>.
70
+ # renders_many :items
71
+
72
+ # @private
73
+ def build_item(component_klass: NavList::Item, **system_arguments)
74
+ component_klass.new(
75
+ **system_arguments,
76
+ selected_item_id: @selected_item_id,
77
+ list: self
78
+ )
79
+ end
80
+
81
+ # @private
82
+ def will_add_item(item)
83
+ item.parent.expand! if item.active? && item.parent
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,25 @@
1
+ declare class NavListElement extends HTMLElement {
2
+ list: HTMLElement;
3
+ showMoreItem: HTMLElement;
4
+ focusMarkers: HTMLElement[];
5
+ connectedCallback(): void;
6
+ get showMoreDisabled(): boolean;
7
+ set showMoreDisabled(value: boolean);
8
+ set currentPage(value: number);
9
+ get currentPage(): number;
10
+ get totalPages(): number;
11
+ get paginationSrc(): string;
12
+ expandItem(item: HTMLElement): void;
13
+ collapseItem(item: HTMLElement): void;
14
+ itemIsExpanded(item: HTMLElement | null): boolean;
15
+ handleItemWithSubItemClick(e: Event): void;
16
+ private showMore;
17
+ private setShowMoreItemState;
18
+ private parseHTML;
19
+ }
20
+ declare global {
21
+ interface Window {
22
+ NavListElement: typeof NavListElement;
23
+ }
24
+ }
25
+ export {};
@@ -0,0 +1,10 @@
1
+ <%= render(Primer::BaseComponent.new(tag: :ul, **@system_arguments)) do %>
2
+ <% sections.each_with_index do |section, index| %>
3
+ <% if index > 0 %>
4
+ <%= render(Primer::Alpha::ActionList::Divider.new) %>
5
+ <% end %>
6
+ <li>
7
+ <%= section %>
8
+ </li>
9
+ <% end %>
10
+ <% end %>