primer_view_components 0.0.121 → 0.0.123
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/assets/styles/primer_view_components.css +2 -2
- data/app/assets/styles/primer_view_components.css.map +1 -1
- data/app/components/primer/alpha/action_list/item.rb +7 -0
- data/app/components/primer/alpha/action_list.css.json +123 -1
- data/app/components/primer/alpha/auto_complete.css.json +23 -1
- data/app/components/primer/alpha/banner.css.json +24 -1
- data/app/components/primer/alpha/button_marketing.css.json +33 -1
- data/app/components/primer/alpha/check_box.rb +74 -0
- data/app/components/primer/alpha/check_box_group.rb +36 -0
- data/app/components/primer/alpha/dialog.css.json +82 -1
- data/app/components/primer/alpha/dialog.rb +1 -1
- data/app/components/primer/alpha/dropdown.css.json +40 -1
- data/app/components/primer/alpha/form_button.rb +32 -0
- data/app/components/primer/alpha/form_control.html.erb +26 -0
- data/app/components/primer/alpha/form_control.rb +105 -0
- data/app/components/primer/alpha/layout.css.json +80 -1
- data/app/components/primer/alpha/menu.css.json +28 -1
- data/app/components/primer/alpha/multi_input.rb +81 -0
- data/app/components/primer/alpha/nav_list/item.rb +4 -0
- data/app/components/primer/alpha/nav_list/section.rb +1 -1
- data/app/components/primer/alpha/nav_list.d.ts +6 -3
- data/app/components/primer/alpha/nav_list.js +95 -6
- data/app/components/primer/alpha/nav_list.rb +5 -0
- data/app/components/primer/alpha/nav_list.ts +105 -3
- data/app/components/primer/alpha/radio_button.rb +25 -0
- data/app/components/primer/alpha/radio_button_group.rb +36 -0
- data/app/components/primer/alpha/segmented_control.css +1 -1
- data/app/components/primer/alpha/segmented_control.css.json +31 -1
- data/app/components/primer/alpha/segmented_control.css.map +1 -1
- data/app/components/primer/alpha/segmented_control.pcss +43 -12
- data/app/components/primer/alpha/select.rb +37 -0
- data/app/components/primer/alpha/submit_button.rb +32 -0
- data/app/components/primer/alpha/tab_nav.css.json +24 -1
- data/app/components/primer/alpha/tab_panels.rb +7 -0
- data/app/components/primer/alpha/text_area.rb +24 -0
- data/app/components/primer/alpha/text_field.css +2 -2
- data/app/components/primer/alpha/text_field.css.json +134 -1
- data/app/components/primer/alpha/text_field.css.map +1 -1
- data/app/components/primer/alpha/text_field.pcss +27 -0
- data/app/components/primer/alpha/text_field.rb +15 -20
- data/app/components/primer/alpha/toggle_switch.css +1 -1
- data/app/components/primer/alpha/toggle_switch.css.json +40 -1
- data/app/components/primer/alpha/toggle_switch.css.map +1 -1
- data/app/components/primer/alpha/toggle_switch.pcss +31 -61
- data/app/components/primer/alpha/underline_nav.css.json +28 -1
- data/app/components/primer/beta/avatar.css.json +17 -1
- data/app/components/primer/beta/avatar_stack.css.json +28 -1
- data/app/components/primer/beta/blankslate.css.json +22 -1
- data/app/components/primer/beta/border_box.css.json +54 -1
- data/app/components/primer/beta/breadcrumbs.css.json +11 -1
- data/app/components/primer/beta/button.css.json +71 -1
- data/app/components/primer/beta/counter.css.json +10 -1
- data/app/components/primer/beta/flash.css.json +27 -1
- data/app/components/primer/beta/label.css.json +25 -1
- data/app/components/primer/beta/link.css.json +19 -1
- data/app/components/primer/beta/popover.css.json +39 -1
- data/app/components/primer/beta/progress_bar.css.json +10 -1
- data/app/components/primer/beta/state.css.json +13 -1
- data/app/components/primer/beta/subhead.css.json +12 -1
- data/app/components/primer/beta/timeline_item.css.json +16 -1
- data/app/components/primer/beta/truncate.css.json +12 -1
- data/app/components/primer/component.rb +10 -2
- data/app/components/primer/truncate.css.json +13 -1
- data/app/forms/{select_list_form.rb → select_form.rb} +1 -1
- data/app/lib/primer/css/layout.css.json +316 -1
- data/app/lib/primer/css/utilities.css.json +1659 -1
- data/lib/primer/form_components.rb +26 -6
- data/lib/primer/forms/builder.rb +1 -17
- data/lib/primer/forms/button.rb +4 -1
- data/lib/primer/forms/check_box_group.html.erb +14 -9
- data/lib/primer/forms/check_box_group.rb +5 -0
- data/lib/primer/forms/dsl/check_box_group_input.rb +3 -4
- data/lib/primer/forms/dsl/input.rb +33 -2
- data/lib/primer/forms/dsl/input_methods.rb +49 -1
- data/lib/primer/forms/dsl/radio_button_group_input.rb +2 -3
- data/lib/primer/forms/dsl/{select_list_input.rb → select_input.rb} +2 -2
- data/lib/primer/forms/dsl/text_field_input.rb +7 -5
- data/lib/primer/forms/form_control.rb +0 -1
- data/lib/primer/forms/group.html.erb +1 -1
- data/lib/primer/forms/multi.html.erb +8 -6
- data/lib/primer/forms/multi.rb +2 -0
- data/lib/primer/forms/radio_button_group.html.erb +14 -9
- data/lib/primer/forms/radio_button_group.rb +5 -0
- data/lib/primer/forms/{select_list.html.erb → select.html.erb} +0 -0
- data/lib/primer/forms/{select_list.rb → select.rb} +2 -2
- data/lib/primer/forms/spacing_wrapper.html.erb +1 -1
- data/lib/primer/forms/text_area.rb +1 -1
- data/lib/primer/forms/text_field.rb +5 -1
- data/lib/primer/forms/utils.rb +20 -0
- data/lib/primer/view_components/engine.rb +1 -1
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/primer/yard/backend.rb +1 -15
- data/lib/primer/yard/component_manifest.rb +44 -25
- data/lib/primer/yard/component_ref.rb +40 -0
- data/lib/primer/yard/docs_helper.rb +16 -2
- data/lib/primer/yard/legacy_gatsby_backend.rb +9 -15
- data/lib/primer/yard/lookbook_docs_helper.rb +32 -0
- data/lib/primer/yard/lookbook_pages_backend.rb +194 -0
- data/lib/primer/yard/registry.rb +6 -21
- data/lib/primer/yard/renders_many_handler.rb +1 -1
- data/lib/primer/yard/renders_one_handler.rb +1 -1
- data/lib/primer/yard.rb +14 -0
- data/lib/tasks/docs.rake +26 -13
- data/previews/pages/forms/01_introduction.md.erb +44 -0
- data/previews/pages/forms/02_getting_started.md.erb +125 -0
- data/previews/pages/forms/03_caption_templates.md.erb +30 -0
- data/previews/pages/forms/04_after_content.md.erb +39 -0
- data/previews/pages/forms/05_groups_layouts.md.erb +22 -0
- data/previews/pages/forms/06_miscellaneous_inputs.md.erb +43 -0
- data/previews/pages/forms/07_toggle_switch_forms.md.erb +58 -0
- data/previews/pages/forms/08_validations.md.erb +28 -0
- data/previews/pages/forms/09_compound_forms.md.erb +97 -0
- data/previews/primer/alpha/check_box_group_preview.rb +89 -0
- data/previews/primer/alpha/check_box_preview.rb +62 -0
- data/previews/primer/alpha/form_control_preview/playground.html.erb +9 -0
- data/previews/primer/alpha/form_control_preview.rb +106 -0
- data/previews/primer/alpha/multi_input_preview/playground.html.erb +41 -0
- data/previews/primer/alpha/multi_input_preview.rb +80 -0
- data/previews/primer/alpha/radio_button_group_preview.rb +83 -0
- data/previews/primer/alpha/radio_button_preview.rb +62 -0
- data/previews/primer/alpha/select_preview.rb +130 -0
- data/previews/primer/alpha/text_area_preview.rb +87 -0
- data/previews/primer/alpha/text_field_preview.rb +10 -1
- data/previews/primer/forms/forms_preview/example_toggle_switch_form.html.erb +2 -2
- data/previews/primer/forms/forms_preview/{select_list_form.html.erb → select_form.html.erb} +1 -1
- data/previews/primer/forms/forms_preview.rb +3 -1
- data/static/arguments.json +1358 -1328
- data/static/audited_at.json +10 -0
- data/static/constants.json +20 -0
- data/static/previews.json +218 -40
- data/static/statuses.json +10 -0
- metadata +41 -7
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Alpha
|
5
|
+
# Wraps an input (or arbitrary content) with a label above and a caption and validation message beneath.
|
6
|
+
# NOTE: This `FormControl` component is designed for wrapping inputs that aren't supported by the Primer
|
7
|
+
# forms framework.
|
8
|
+
class FormControl < Primer::Component
|
9
|
+
# Describes the field and what sorts of input it expects. Displayed below the input.
|
10
|
+
# Note that this slot takes precedence over the `caption:` argument in the constructor.
|
11
|
+
renders_one :caption
|
12
|
+
|
13
|
+
# @example Default
|
14
|
+
# <%= render(Primer::Alpha::FormControl.new(label: "Best character")) do |component| %>
|
15
|
+
# <% component.with_input do |input_arguments| %>
|
16
|
+
# <%= render(Primer::Alpha::SegmentedControl.new("aria-label": "Best character", **input_arguments)) do |seg| %>
|
17
|
+
# <% seg.with_item(label: "Han Solo") %>
|
18
|
+
# <% seg.with_item(label: "Luke Skywalker") %>
|
19
|
+
# <% seg.with_item(label: "Leia Organa") %>
|
20
|
+
# <% end %>
|
21
|
+
# <% end %>
|
22
|
+
# <% end %>
|
23
|
+
#
|
24
|
+
# @param label [String] Label text displayed above the input.
|
25
|
+
# @param caption [String] Describes the field and what sort of input it expects. Displayed below the input. Note that the `caption` slot is also available and takes precedence over this argument when provided.
|
26
|
+
# @param validation_message [String] A string displayed in red between the caption and the input indicating the input's contents are invalid.
|
27
|
+
# @param required [Boolean] Default `false`. When set to `true`, causes an asterisk (*) to appear next to the field's label indicating it is a required field. Note that this option explicitly does _not_ add a `required` HTML attribute. Doing so would enable native browser validations, which are inaccessible and inconsistent with the Primer design system.
|
28
|
+
# @param visually_hide_label [Boolean] When set to `true`, hides the label. Although the label will be hidden visually, it will still be visible to screen readers.
|
29
|
+
# @param full_width [Boolean] When set to `true`, the form control will take up all the horizontal space allowed by its container.
|
30
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
31
|
+
def initialize(label:, caption: nil, validation_message: nil, required: false, visually_hide_label: false, full_width: false, **system_arguments)
|
32
|
+
@label = label
|
33
|
+
@init_caption = caption
|
34
|
+
@validation_message = validation_message
|
35
|
+
@required = required
|
36
|
+
@visually_hide_label = visually_hide_label
|
37
|
+
@full_width = full_width
|
38
|
+
@system_arguments = system_arguments
|
39
|
+
|
40
|
+
@system_arguments[:classes] = class_names(
|
41
|
+
@system_arguments[:classes],
|
42
|
+
"FormControl",
|
43
|
+
"FormControl--fullWidth" => full_width?
|
44
|
+
)
|
45
|
+
|
46
|
+
@label_arguments = {
|
47
|
+
classes: class_names(
|
48
|
+
"FormControl-label",
|
49
|
+
visually_hide_label? ? "sr-only" : nil
|
50
|
+
)
|
51
|
+
}
|
52
|
+
|
53
|
+
base_id = self.class.generate_id
|
54
|
+
@validation_id = "validation-#{base_id}"
|
55
|
+
@caption_id = "caption-#{base_id}"
|
56
|
+
|
57
|
+
@validation_arguments = {
|
58
|
+
classes: "FormControl-inlineValidation",
|
59
|
+
id: @validation_id
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
# @!parse
|
64
|
+
# # The input content. Yields a set of <%= link_to_system_arguments_docs %> that should be added to the input.
|
65
|
+
# #
|
66
|
+
# renders_one(:input)
|
67
|
+
|
68
|
+
def with_input(&block)
|
69
|
+
@input_block = block
|
70
|
+
end
|
71
|
+
|
72
|
+
def required?
|
73
|
+
@required
|
74
|
+
end
|
75
|
+
|
76
|
+
def visually_hide_label?
|
77
|
+
@visually_hide_label
|
78
|
+
end
|
79
|
+
|
80
|
+
def full_width?
|
81
|
+
@full_width
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def before_render
|
87
|
+
# make sure to evaluate the component's content block so slots are defined
|
88
|
+
content
|
89
|
+
|
90
|
+
@input_arguments = {
|
91
|
+
aria: {}
|
92
|
+
}
|
93
|
+
|
94
|
+
ids = [].tap do |memo|
|
95
|
+
memo << @validation_id if @validation_message
|
96
|
+
memo << @caption_id if @init_caption || caption?
|
97
|
+
end
|
98
|
+
|
99
|
+
return if ids.empty?
|
100
|
+
|
101
|
+
@input_arguments[:aria][:describedby] = ids.join(" ")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -1 +1,80 @@
|
|
1
|
-
{
|
1
|
+
{
|
2
|
+
"name": "alpha/layout",
|
3
|
+
"selectors": [
|
4
|
+
".Layout",
|
5
|
+
".Layout .Layout-divider",
|
6
|
+
".Layout .Layout-main",
|
7
|
+
".Layout .Layout-sidebar",
|
8
|
+
".Layout.Layout--sidebarPosition-flowRow-start .Layout-sidebar",
|
9
|
+
".Layout.Layout--sidebarPosition-flowRow-end .Layout-sidebar",
|
10
|
+
".Layout.Layout--sidebarPosition-flowRow-start .Layout-main",
|
11
|
+
".Layout.Layout--sidebarPosition-flowRow-end .Layout-main",
|
12
|
+
".Layout.Layout--sidebarPosition-flowRow-none .Layout-sidebar",
|
13
|
+
".Layout.Layout--divided",
|
14
|
+
".Layout.Layout--divided .Layout-divider",
|
15
|
+
".Layout.Layout--divided .Layout-divider.Layout-divider--flowRow-hidden",
|
16
|
+
".Layout.Layout--divided .Layout-divider.Layout-divider--flowRow-shallow",
|
17
|
+
".Layout.Layout--divided .Layout-main",
|
18
|
+
".Layout.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-sidebar",
|
19
|
+
".Layout.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-main",
|
20
|
+
".Layout.Layout--flowRow-until-md",
|
21
|
+
".Layout.Layout--flowRow-until-md .Layout-divider",
|
22
|
+
".Layout.Layout--flowRow-until-md .Layout-main",
|
23
|
+
".Layout.Layout--flowRow-until-md .Layout-sidebar",
|
24
|
+
".Layout.Layout--flowRow-until-md.Layout--sidebarPosition-flowRow-start .Layout-sidebar",
|
25
|
+
".Layout.Layout--flowRow-until-md.Layout--sidebarPosition-flowRow-end .Layout-sidebar",
|
26
|
+
".Layout.Layout--flowRow-until-md.Layout--sidebarPosition-flowRow-start .Layout-main",
|
27
|
+
".Layout.Layout--flowRow-until-md.Layout--sidebarPosition-flowRow-end .Layout-main",
|
28
|
+
".Layout.Layout--flowRow-until-md.Layout--sidebarPosition-flowRow-none .Layout-sidebar",
|
29
|
+
".Layout.Layout--flowRow-until-md.Layout--divided",
|
30
|
+
".Layout.Layout--flowRow-until-md.Layout--divided .Layout-divider",
|
31
|
+
".Layout.Layout--flowRow-until-md.Layout--divided .Layout-divider.Layout-divider--flowRow-hidden",
|
32
|
+
".Layout.Layout--flowRow-until-md.Layout--divided .Layout-divider.Layout-divider--flowRow-shallow",
|
33
|
+
".Layout.Layout--flowRow-until-md.Layout--divided .Layout-main",
|
34
|
+
".Layout.Layout--flowRow-until-md.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-sidebar",
|
35
|
+
".Layout.Layout--flowRow-until-md.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-main",
|
36
|
+
".Layout.Layout--flowRow-until-lg",
|
37
|
+
".Layout.Layout--flowRow-until-lg .Layout-divider",
|
38
|
+
".Layout.Layout--flowRow-until-lg .Layout-main",
|
39
|
+
".Layout.Layout--flowRow-until-lg .Layout-sidebar",
|
40
|
+
".Layout.Layout--flowRow-until-lg.Layout--sidebarPosition-flowRow-start .Layout-sidebar",
|
41
|
+
".Layout.Layout--flowRow-until-lg.Layout--sidebarPosition-flowRow-end .Layout-sidebar",
|
42
|
+
".Layout.Layout--flowRow-until-lg.Layout--sidebarPosition-flowRow-start .Layout-main",
|
43
|
+
".Layout.Layout--flowRow-until-lg.Layout--sidebarPosition-flowRow-end .Layout-main",
|
44
|
+
".Layout.Layout--flowRow-until-lg.Layout--sidebarPosition-flowRow-none .Layout-sidebar",
|
45
|
+
".Layout.Layout--flowRow-until-lg.Layout--divided",
|
46
|
+
".Layout.Layout--flowRow-until-lg.Layout--divided .Layout-divider",
|
47
|
+
".Layout.Layout--flowRow-until-lg.Layout--divided .Layout-divider.Layout-divider--flowRow-hidden",
|
48
|
+
".Layout.Layout--flowRow-until-lg.Layout--divided .Layout-divider.Layout-divider--flowRow-shallow",
|
49
|
+
".Layout.Layout--flowRow-until-lg.Layout--divided .Layout-main",
|
50
|
+
".Layout.Layout--flowRow-until-lg.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-sidebar",
|
51
|
+
".Layout.Layout--flowRow-until-lg.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-main",
|
52
|
+
".Layout.Layout--gutter-none",
|
53
|
+
".Layout.Layout--gutter-condensed",
|
54
|
+
".Layout.Layout--gutter-spacious",
|
55
|
+
".Layout.Layout--sidebar-narrow",
|
56
|
+
".Layout.Layout--sidebar-wide",
|
57
|
+
".Layout.Layout--sidebarPosition-start .Layout-sidebar",
|
58
|
+
".Layout.Layout--sidebarPosition-start .Layout-main",
|
59
|
+
".Layout.Layout--sidebarPosition-end",
|
60
|
+
".Layout.Layout--sidebarPosition-end .Layout-main",
|
61
|
+
".Layout.Layout--sidebarPosition-end .Layout-sidebar",
|
62
|
+
".Layout.Layout--divided.Layout--sidebarPosition-end .Layout-sidebar",
|
63
|
+
".Layout.Layout--divided.Layout--sidebarPosition-end .Layout-main",
|
64
|
+
".Layout-divider",
|
65
|
+
".Layout-sidebar",
|
66
|
+
".Layout-main",
|
67
|
+
".Layout-main .Layout-main-centered-lg",
|
68
|
+
".Layout-main .Layout-main-centered-md",
|
69
|
+
".Layout-main .Layout-main-centered-xl",
|
70
|
+
".Layout-main .Layout-main-centered-lg>.container-lg",
|
71
|
+
".Layout-main .Layout-main-centered-lg>.container-md",
|
72
|
+
".Layout-main .Layout-main-centered-lg>.container-xl",
|
73
|
+
".Layout-main .Layout-main-centered-md>.container-lg",
|
74
|
+
".Layout-main .Layout-main-centered-md>.container-md",
|
75
|
+
".Layout-main .Layout-main-centered-md>.container-xl",
|
76
|
+
".Layout-main .Layout-main-centered-xl>.container-lg",
|
77
|
+
".Layout-main .Layout-main-centered-xl>.container-md",
|
78
|
+
".Layout-main .Layout-main-centered-xl>.container-xl"
|
79
|
+
]
|
80
|
+
}
|
@@ -1 +1,28 @@
|
|
1
|
-
{
|
1
|
+
{
|
2
|
+
"name": "alpha/menu",
|
3
|
+
"selectors": [
|
4
|
+
".menu",
|
5
|
+
".menu-item",
|
6
|
+
".menu-item:first-child",
|
7
|
+
".menu-item:first-child:before",
|
8
|
+
".menu-item:last-child",
|
9
|
+
".menu-item:last-child:before",
|
10
|
+
".menu-item:hover",
|
11
|
+
".menu-item:active",
|
12
|
+
".menu-item.selected",
|
13
|
+
".menu-item[aria-current]:not([aria-current=false])",
|
14
|
+
".menu-item[aria-selected=true]",
|
15
|
+
".menu-item.selected:before",
|
16
|
+
".menu-item[aria-current]:not([aria-current=false]):before",
|
17
|
+
".menu-item[aria-selected=true]:before",
|
18
|
+
".menu-item .octicon",
|
19
|
+
".menu-item .Counter",
|
20
|
+
".menu-item .menu-warning",
|
21
|
+
".menu-item .avatar",
|
22
|
+
".menu-item.alert .Counter",
|
23
|
+
".menu-heading",
|
24
|
+
".menu-heading:hover",
|
25
|
+
".menu-heading:first-child",
|
26
|
+
".menu-heading:last-child"
|
27
|
+
]
|
28
|
+
}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Alpha
|
5
|
+
MultiInput = Primer::FormComponents.from_input(Primer::Forms::Dsl::MultiInput)
|
6
|
+
|
7
|
+
# Multi inputs are comprised of multiple constituent fields, only one of which is visible
|
8
|
+
# at a given time. They are designed for situations where constituent inputs are shown or
|
9
|
+
# hidden based on the value of another field. For example, consider an address form. If
|
10
|
+
# the user chooses the USA as the country, the region input should show a list of states
|
11
|
+
# and US territories; if the user instead chooses Canada, the region input should show a
|
12
|
+
# list of Canadian provinces, etc.
|
13
|
+
#
|
14
|
+
# Unlike everywhere else in Primer forms, constituent inputs are not directly passed a
|
15
|
+
# `name` attribute. Instead, developers pass a `name` attribute to the multi input itself.
|
16
|
+
# The multi input then automatically assigns each constituent input the same name. This is
|
17
|
+
# done so that the multi input looks like a single field from the server's point of view.
|
18
|
+
# Using the address form example from earlier, this means only one value - either a US state
|
19
|
+
# or a Canadian provice - will be submitted to the server under the `region` key.
|
20
|
+
#
|
21
|
+
# Actually, that's not quite true. Constituent inputs _are_ given a `name`, but it's added to
|
22
|
+
# the input as the `data-name` attribute as a way to identify constituent inputs, and will not
|
23
|
+
# be sent to the server.
|
24
|
+
#
|
25
|
+
# @form_usage
|
26
|
+
# class ExampleForm < ApplicationForm
|
27
|
+
# form do |example_form|
|
28
|
+
# example_form.multi(attributes) do |multi|
|
29
|
+
# # can define any number of child inputs, for example:
|
30
|
+
# multi.text_field(text_field_attributes)
|
31
|
+
# multi.select_list(select_list_attributes, hidden: true) do |list|
|
32
|
+
# list.option(option_attributes)
|
33
|
+
# list.option(option_attributes)
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
class MultiInput < Primer::Component
|
39
|
+
# @!parse include Primer::Forms::Dsl::InputMethods
|
40
|
+
|
41
|
+
status :alpha
|
42
|
+
|
43
|
+
# @!method initialize
|
44
|
+
#
|
45
|
+
# @example Default
|
46
|
+
# <%= render(Primer::Alpha::Select.new(name: :dietary_pref, label: "Dietary preference")) do |c| %>
|
47
|
+
# <% c.option(label: "Meatatarian", value: "meatatarian") %>
|
48
|
+
# <% c.option(label: "Vegetarian", value: "vegetarian") %>
|
49
|
+
# <% end %>
|
50
|
+
#
|
51
|
+
# <%= render(Primer::Alpha::MultiInput.new(name: :dish, label: "Select dish")) do |c| %>
|
52
|
+
# <% c.select_list(name: :meatatarian) do |list| %>
|
53
|
+
# <% list.option(label: "Steak", value: "steak") %>
|
54
|
+
# <% list.option(label: "Salmon", value: "salmon") %>
|
55
|
+
# <% end %>
|
56
|
+
# <% c.select_list(name: :vegetarian, hidden: true) do |list| %>
|
57
|
+
# <% list.option(label: "Portobello mushroom", value: "portobello") %>
|
58
|
+
# <% list.option(label: "Tofu curry", value: "tofu") %>
|
59
|
+
# <% end %>
|
60
|
+
# <% end %>
|
61
|
+
#
|
62
|
+
# <script type="text/javascript" data-eval="true">
|
63
|
+
# const dietaryPrefList = document.querySelector("[name=dietary_pref]");
|
64
|
+
# const dishMulti = document.querySelector("[data-name=dish]");
|
65
|
+
#
|
66
|
+
# dietaryPrefList.onchange = (evt) => {
|
67
|
+
# switch (evt.target.value) {
|
68
|
+
# case 'meatatarian':
|
69
|
+
# dishMulti.activateField('meatatarian');
|
70
|
+
# break;
|
71
|
+
# case 'vegetarian':
|
72
|
+
# dishMulti.activateField('vegetarian');
|
73
|
+
# break;
|
74
|
+
# }
|
75
|
+
# };
|
76
|
+
# </script>
|
77
|
+
#
|
78
|
+
# @macro form_input_arguments
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
|
-
declare class NavListElement extends HTMLElement {
|
1
|
+
export declare class NavListElement extends HTMLElement {
|
2
|
+
#private;
|
2
3
|
list: HTMLElement;
|
4
|
+
items: HTMLElement[];
|
3
5
|
showMoreItem: HTMLElement;
|
4
6
|
focusMarkers: HTMLElement[];
|
5
7
|
connectedCallback(): void;
|
@@ -9,17 +11,18 @@ declare class NavListElement extends HTMLElement {
|
|
9
11
|
get currentPage(): number;
|
10
12
|
get totalPages(): number;
|
11
13
|
get paginationSrc(): string;
|
14
|
+
selectItemById(itemId: string | null): boolean;
|
15
|
+
selectItemByHref(href: string | null): boolean;
|
16
|
+
selectItemByCurrentLocation(): boolean;
|
12
17
|
expandItem(item: HTMLElement): void;
|
13
18
|
collapseItem(item: HTMLElement): void;
|
14
19
|
itemIsExpanded(item: HTMLElement | null): boolean;
|
15
20
|
handleItemWithSubItemClick(e: Event): void;
|
16
21
|
private showMore;
|
17
22
|
private setShowMoreItemState;
|
18
|
-
private parseHTML;
|
19
23
|
}
|
20
24
|
declare global {
|
21
25
|
interface Window {
|
22
26
|
NavListElement: typeof NavListElement;
|
23
27
|
}
|
24
28
|
}
|
25
|
-
export {};
|
@@ -4,9 +4,19 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
6
6
|
};
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
11
|
+
};
|
12
|
+
var _NavListElement_instances, _NavListElement_parseHTML, _NavListElement_findSelectedNavItemById, _NavListElement_findSelectedNavItemByHref, _NavListElement_findSelectedNavItemByCurrentLocation, _NavListElement_select, _NavListElement_deselect, _NavListElement_findParentMenu;
|
7
13
|
/* eslint-disable custom-elements/expose-class-on-global */
|
8
14
|
import { controller, target, targets } from '@github/catalyst';
|
9
15
|
let NavListElement = class NavListElement extends HTMLElement {
|
16
|
+
constructor() {
|
17
|
+
super(...arguments);
|
18
|
+
_NavListElement_instances.add(this);
|
19
|
+
}
|
10
20
|
connectedCallback() {
|
11
21
|
this.setShowMoreItemState();
|
12
22
|
}
|
@@ -34,6 +44,34 @@ let NavListElement = class NavListElement extends HTMLElement {
|
|
34
44
|
get paginationSrc() {
|
35
45
|
return this.showMoreItem.getAttribute('src') || '';
|
36
46
|
}
|
47
|
+
selectItemById(itemId) {
|
48
|
+
if (!itemId)
|
49
|
+
return false;
|
50
|
+
const selectedItem = __classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_findSelectedNavItemById).call(this, itemId);
|
51
|
+
if (selectedItem) {
|
52
|
+
__classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_select).call(this, selectedItem);
|
53
|
+
return true;
|
54
|
+
}
|
55
|
+
return false;
|
56
|
+
}
|
57
|
+
selectItemByHref(href) {
|
58
|
+
if (!href)
|
59
|
+
return false;
|
60
|
+
const selectedItem = __classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_findSelectedNavItemByHref).call(this, href);
|
61
|
+
if (selectedItem) {
|
62
|
+
__classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_select).call(this, selectedItem);
|
63
|
+
return true;
|
64
|
+
}
|
65
|
+
return false;
|
66
|
+
}
|
67
|
+
selectItemByCurrentLocation() {
|
68
|
+
const selectedItem = __classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_findSelectedNavItemByCurrentLocation).call(this);
|
69
|
+
if (selectedItem) {
|
70
|
+
__classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_select).call(this, selectedItem);
|
71
|
+
return true;
|
72
|
+
}
|
73
|
+
return false;
|
74
|
+
}
|
37
75
|
// expand collapsible item onClick
|
38
76
|
expandItem(item) {
|
39
77
|
var _a;
|
@@ -92,7 +130,7 @@ let NavListElement = class NavListElement extends HTMLElement {
|
|
92
130
|
this.currentPage--;
|
93
131
|
return;
|
94
132
|
}
|
95
|
-
const fragment = this.
|
133
|
+
const fragment = __classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_parseHTML).call(this, document, html);
|
96
134
|
(_a = fragment === null || fragment === void 0 ? void 0 : fragment.querySelector('li > a')) === null || _a === void 0 ? void 0 : _a.setAttribute('data-targets', 'nav-list.focusMarkers');
|
97
135
|
this.list.insertBefore(fragment, this.showMoreItem);
|
98
136
|
(_b = this.focusMarkers.pop()) === null || _b === void 0 ? void 0 : _b.focus();
|
@@ -109,16 +147,66 @@ let NavListElement = class NavListElement extends HTMLElement {
|
|
109
147
|
this.showMoreItem.hidden = true;
|
110
148
|
}
|
111
149
|
}
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
150
|
+
};
|
151
|
+
_NavListElement_instances = new WeakSet(), _NavListElement_parseHTML = function _NavListElement_parseHTML(document, html) {
|
152
|
+
const template = document.createElement('template');
|
153
|
+
// eslint-disable-next-line github/no-inner-html
|
154
|
+
template.innerHTML = html;
|
155
|
+
return document.importNode(template.content, true);
|
156
|
+
}, _NavListElement_findSelectedNavItemById = function _NavListElement_findSelectedNavItemById(itemId) {
|
157
|
+
var _a;
|
158
|
+
// First we compare the selected link to data-item-id for each nav item
|
159
|
+
for (const navItem of this.items) {
|
160
|
+
const keys = ((_a = navItem.getAttribute('data-item-id')) === null || _a === void 0 ? void 0 : _a.split(' ')) || [];
|
161
|
+
if (keys.includes(itemId)) {
|
162
|
+
return navItem;
|
163
|
+
}
|
164
|
+
}
|
165
|
+
return null;
|
166
|
+
}, _NavListElement_findSelectedNavItemByHref = function _NavListElement_findSelectedNavItemByHref(href) {
|
167
|
+
// If we didn't find a match, we compare the selected link to the href of each nav item
|
168
|
+
const selectedNavItem = this.querySelector(`.ActionListContent[href="${href}"]`);
|
169
|
+
if (selectedNavItem) {
|
170
|
+
return selectedNavItem.closest('.ActionListItem');
|
171
|
+
}
|
172
|
+
return null;
|
173
|
+
}, _NavListElement_findSelectedNavItemByCurrentLocation = function _NavListElement_findSelectedNavItemByCurrentLocation() {
|
174
|
+
return __classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_findSelectedNavItemByHref).call(this, window.location.pathname);
|
175
|
+
}, _NavListElement_select = function _NavListElement_select(navItem) {
|
176
|
+
const currentlySelectedItem = this.querySelector('.ActionListItem--navActive');
|
177
|
+
if (currentlySelectedItem)
|
178
|
+
__classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_deselect).call(this, currentlySelectedItem);
|
179
|
+
navItem.classList.add('ActionListItem--navActive');
|
180
|
+
const parentMenu = __classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_findParentMenu).call(this, navItem);
|
181
|
+
if (parentMenu) {
|
182
|
+
this.expandItem(parentMenu);
|
183
|
+
parentMenu.classList.add('ActionListContent--hasActiveSubItem');
|
184
|
+
}
|
185
|
+
}, _NavListElement_deselect = function _NavListElement_deselect(navItem) {
|
186
|
+
navItem.classList.remove('ActionListItem--navActive');
|
187
|
+
const parentMenu = __classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_findParentMenu).call(this, navItem);
|
188
|
+
if (parentMenu) {
|
189
|
+
this.collapseItem(parentMenu);
|
190
|
+
parentMenu.classList.remove('ActionListContent--hasActiveSubItem');
|
191
|
+
}
|
192
|
+
}, _NavListElement_findParentMenu = function _NavListElement_findParentMenu(navItem) {
|
193
|
+
var _a;
|
194
|
+
if (!navItem.classList.contains('ActionListItem--subItem'))
|
195
|
+
return null;
|
196
|
+
const parent = (_a = navItem.closest('li.ActionListItem--hasSubItem')) === null || _a === void 0 ? void 0 : _a.querySelector('button.ActionListContent');
|
197
|
+
if (parent) {
|
198
|
+
return parent;
|
199
|
+
}
|
200
|
+
else {
|
201
|
+
return null;
|
117
202
|
}
|
118
203
|
};
|
119
204
|
__decorate([
|
120
205
|
target
|
121
206
|
], NavListElement.prototype, "list", void 0);
|
207
|
+
__decorate([
|
208
|
+
targets
|
209
|
+
], NavListElement.prototype, "items", void 0);
|
122
210
|
__decorate([
|
123
211
|
target
|
124
212
|
], NavListElement.prototype, "showMoreItem", void 0);
|
@@ -128,3 +216,4 @@ __decorate([
|
|
128
216
|
NavListElement = __decorate([
|
129
217
|
controller
|
130
218
|
], NavListElement);
|
219
|
+
export { NavListElement };
|
@@ -17,6 +17,11 @@ module Primer
|
|
17
17
|
class NavList < Primer::Component
|
18
18
|
status :alpha
|
19
19
|
|
20
|
+
# @private
|
21
|
+
def self.custom_element_name
|
22
|
+
"nav-list"
|
23
|
+
end
|
24
|
+
|
20
25
|
# Sections. Each section is a list of links and an optional heading.
|
21
26
|
#
|
22
27
|
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::NavList::Section) %>.
|
@@ -2,8 +2,9 @@
|
|
2
2
|
import {controller, target, targets} from '@github/catalyst'
|
3
3
|
|
4
4
|
@controller
|
5
|
-
class NavListElement extends HTMLElement {
|
5
|
+
export class NavListElement extends HTMLElement {
|
6
6
|
@target list: HTMLElement
|
7
|
+
@targets items: HTMLElement[]
|
7
8
|
@target showMoreItem: HTMLElement
|
8
9
|
@targets focusMarkers: HTMLElement[]
|
9
10
|
|
@@ -40,6 +41,43 @@ class NavListElement extends HTMLElement {
|
|
40
41
|
return this.showMoreItem.getAttribute('src') || ''
|
41
42
|
}
|
42
43
|
|
44
|
+
selectItemById(itemId: string | null): boolean {
|
45
|
+
if (!itemId) return false
|
46
|
+
|
47
|
+
const selectedItem = this.#findSelectedNavItemById(itemId)
|
48
|
+
|
49
|
+
if (selectedItem) {
|
50
|
+
this.#select(selectedItem)
|
51
|
+
return true
|
52
|
+
}
|
53
|
+
|
54
|
+
return false
|
55
|
+
}
|
56
|
+
|
57
|
+
selectItemByHref(href: string | null): boolean {
|
58
|
+
if (!href) return false
|
59
|
+
|
60
|
+
const selectedItem = this.#findSelectedNavItemByHref(href)
|
61
|
+
|
62
|
+
if (selectedItem) {
|
63
|
+
this.#select(selectedItem)
|
64
|
+
return true
|
65
|
+
}
|
66
|
+
|
67
|
+
return false
|
68
|
+
}
|
69
|
+
|
70
|
+
selectItemByCurrentLocation(): boolean {
|
71
|
+
const selectedItem = this.#findSelectedNavItemByCurrentLocation()
|
72
|
+
|
73
|
+
if (selectedItem) {
|
74
|
+
this.#select(selectedItem)
|
75
|
+
return true
|
76
|
+
}
|
77
|
+
|
78
|
+
return false
|
79
|
+
}
|
80
|
+
|
43
81
|
// expand collapsible item onClick
|
44
82
|
expandItem(item: HTMLElement) {
|
45
83
|
item.nextElementSibling?.removeAttribute('data-hidden')
|
@@ -95,7 +133,7 @@ class NavListElement extends HTMLElement {
|
|
95
133
|
this.currentPage--
|
96
134
|
return
|
97
135
|
}
|
98
|
-
const fragment = this
|
136
|
+
const fragment = this.#parseHTML(document, html)
|
99
137
|
fragment?.querySelector('li > a')?.setAttribute('data-targets', 'nav-list.focusMarkers')
|
100
138
|
this.list.insertBefore(fragment, this.showMoreItem)
|
101
139
|
this.focusMarkers.pop()?.focus()
|
@@ -114,12 +152,76 @@ class NavListElement extends HTMLElement {
|
|
114
152
|
}
|
115
153
|
}
|
116
154
|
|
117
|
-
|
155
|
+
#parseHTML(document: Document, html: string): DocumentFragment {
|
118
156
|
const template = document.createElement('template')
|
119
157
|
// eslint-disable-next-line github/no-inner-html
|
120
158
|
template.innerHTML = html
|
121
159
|
return document.importNode(template.content, true)
|
122
160
|
}
|
161
|
+
|
162
|
+
#findSelectedNavItemById(itemId: string): HTMLElement | null {
|
163
|
+
// First we compare the selected link to data-item-id for each nav item
|
164
|
+
for (const navItem of this.items) {
|
165
|
+
const keys = navItem.getAttribute('data-item-id')?.split(' ') || []
|
166
|
+
|
167
|
+
if (keys.includes(itemId)) {
|
168
|
+
return navItem
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
return null
|
173
|
+
}
|
174
|
+
|
175
|
+
#findSelectedNavItemByHref(href: string): HTMLElement | null {
|
176
|
+
// If we didn't find a match, we compare the selected link to the href of each nav item
|
177
|
+
const selectedNavItem = this.querySelector<HTMLAnchorElement>(`.ActionListContent[href="${href}"]`)
|
178
|
+
if (selectedNavItem) {
|
179
|
+
return selectedNavItem.closest('.ActionListItem')
|
180
|
+
}
|
181
|
+
|
182
|
+
return null
|
183
|
+
}
|
184
|
+
|
185
|
+
#findSelectedNavItemByCurrentLocation(): HTMLElement | null {
|
186
|
+
return this.#findSelectedNavItemByHref(window.location.pathname)
|
187
|
+
}
|
188
|
+
|
189
|
+
#select(navItem: HTMLElement) {
|
190
|
+
const currentlySelectedItem = this.querySelector('.ActionListItem--navActive') as HTMLElement
|
191
|
+
if (currentlySelectedItem) this.#deselect(currentlySelectedItem)
|
192
|
+
|
193
|
+
navItem.classList.add('ActionListItem--navActive')
|
194
|
+
|
195
|
+
const parentMenu = this.#findParentMenu(navItem)
|
196
|
+
|
197
|
+
if (parentMenu) {
|
198
|
+
this.expandItem(parentMenu)
|
199
|
+
parentMenu.classList.add('ActionListContent--hasActiveSubItem')
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
#deselect(navItem: HTMLElement) {
|
204
|
+
navItem.classList.remove('ActionListItem--navActive')
|
205
|
+
|
206
|
+
const parentMenu = this.#findParentMenu(navItem)
|
207
|
+
|
208
|
+
if (parentMenu) {
|
209
|
+
this.collapseItem(parentMenu)
|
210
|
+
parentMenu.classList.remove('ActionListContent--hasActiveSubItem')
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
#findParentMenu(navItem: HTMLElement): HTMLElement | null {
|
215
|
+
if (!navItem.classList.contains('ActionListItem--subItem')) return null
|
216
|
+
|
217
|
+
const parent = navItem.closest('li.ActionListItem--hasSubItem')?.querySelector('button.ActionListContent')
|
218
|
+
|
219
|
+
if (parent) {
|
220
|
+
return parent as HTMLElement
|
221
|
+
} else {
|
222
|
+
return null
|
223
|
+
}
|
224
|
+
}
|
123
225
|
}
|
124
226
|
|
125
227
|
declare global {
|