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.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -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 +2 -2
  6. data/app/assets/styles/primer_view_components.css.map +1 -1
  7. data/app/components/primer/alpha/action_list/item.rb +7 -0
  8. data/app/components/primer/alpha/action_list.css.json +123 -1
  9. data/app/components/primer/alpha/auto_complete.css.json +23 -1
  10. data/app/components/primer/alpha/banner.css.json +24 -1
  11. data/app/components/primer/alpha/button_marketing.css.json +33 -1
  12. data/app/components/primer/alpha/check_box.rb +74 -0
  13. data/app/components/primer/alpha/check_box_group.rb +36 -0
  14. data/app/components/primer/alpha/dialog.css.json +82 -1
  15. data/app/components/primer/alpha/dialog.rb +1 -1
  16. data/app/components/primer/alpha/dropdown.css.json +40 -1
  17. data/app/components/primer/alpha/form_button.rb +32 -0
  18. data/app/components/primer/alpha/form_control.html.erb +26 -0
  19. data/app/components/primer/alpha/form_control.rb +105 -0
  20. data/app/components/primer/alpha/layout.css.json +80 -1
  21. data/app/components/primer/alpha/menu.css.json +28 -1
  22. data/app/components/primer/alpha/multi_input.rb +81 -0
  23. data/app/components/primer/alpha/nav_list/item.rb +4 -0
  24. data/app/components/primer/alpha/nav_list/section.rb +1 -1
  25. data/app/components/primer/alpha/nav_list.d.ts +6 -3
  26. data/app/components/primer/alpha/nav_list.js +95 -6
  27. data/app/components/primer/alpha/nav_list.rb +5 -0
  28. data/app/components/primer/alpha/nav_list.ts +105 -3
  29. data/app/components/primer/alpha/radio_button.rb +25 -0
  30. data/app/components/primer/alpha/radio_button_group.rb +36 -0
  31. data/app/components/primer/alpha/segmented_control.css +1 -1
  32. data/app/components/primer/alpha/segmented_control.css.json +31 -1
  33. data/app/components/primer/alpha/segmented_control.css.map +1 -1
  34. data/app/components/primer/alpha/segmented_control.pcss +43 -12
  35. data/app/components/primer/alpha/select.rb +37 -0
  36. data/app/components/primer/alpha/submit_button.rb +32 -0
  37. data/app/components/primer/alpha/tab_nav.css.json +24 -1
  38. data/app/components/primer/alpha/tab_panels.rb +7 -0
  39. data/app/components/primer/alpha/text_area.rb +24 -0
  40. data/app/components/primer/alpha/text_field.css +2 -2
  41. data/app/components/primer/alpha/text_field.css.json +134 -1
  42. data/app/components/primer/alpha/text_field.css.map +1 -1
  43. data/app/components/primer/alpha/text_field.pcss +27 -0
  44. data/app/components/primer/alpha/text_field.rb +15 -20
  45. data/app/components/primer/alpha/toggle_switch.css +1 -1
  46. data/app/components/primer/alpha/toggle_switch.css.json +40 -1
  47. data/app/components/primer/alpha/toggle_switch.css.map +1 -1
  48. data/app/components/primer/alpha/toggle_switch.pcss +31 -61
  49. data/app/components/primer/alpha/underline_nav.css.json +28 -1
  50. data/app/components/primer/beta/avatar.css.json +17 -1
  51. data/app/components/primer/beta/avatar_stack.css.json +28 -1
  52. data/app/components/primer/beta/blankslate.css.json +22 -1
  53. data/app/components/primer/beta/border_box.css.json +54 -1
  54. data/app/components/primer/beta/breadcrumbs.css.json +11 -1
  55. data/app/components/primer/beta/button.css.json +71 -1
  56. data/app/components/primer/beta/counter.css.json +10 -1
  57. data/app/components/primer/beta/flash.css.json +27 -1
  58. data/app/components/primer/beta/label.css.json +25 -1
  59. data/app/components/primer/beta/link.css.json +19 -1
  60. data/app/components/primer/beta/popover.css.json +39 -1
  61. data/app/components/primer/beta/progress_bar.css.json +10 -1
  62. data/app/components/primer/beta/state.css.json +13 -1
  63. data/app/components/primer/beta/subhead.css.json +12 -1
  64. data/app/components/primer/beta/timeline_item.css.json +16 -1
  65. data/app/components/primer/beta/truncate.css.json +12 -1
  66. data/app/components/primer/component.rb +10 -2
  67. data/app/components/primer/truncate.css.json +13 -1
  68. data/app/forms/{select_list_form.rb → select_form.rb} +1 -1
  69. data/app/lib/primer/css/layout.css.json +316 -1
  70. data/app/lib/primer/css/utilities.css.json +1659 -1
  71. data/lib/primer/form_components.rb +26 -6
  72. data/lib/primer/forms/builder.rb +1 -17
  73. data/lib/primer/forms/button.rb +4 -1
  74. data/lib/primer/forms/check_box_group.html.erb +14 -9
  75. data/lib/primer/forms/check_box_group.rb +5 -0
  76. data/lib/primer/forms/dsl/check_box_group_input.rb +3 -4
  77. data/lib/primer/forms/dsl/input.rb +33 -2
  78. data/lib/primer/forms/dsl/input_methods.rb +49 -1
  79. data/lib/primer/forms/dsl/radio_button_group_input.rb +2 -3
  80. data/lib/primer/forms/dsl/{select_list_input.rb → select_input.rb} +2 -2
  81. data/lib/primer/forms/dsl/text_field_input.rb +7 -5
  82. data/lib/primer/forms/form_control.rb +0 -1
  83. data/lib/primer/forms/group.html.erb +1 -1
  84. data/lib/primer/forms/multi.html.erb +8 -6
  85. data/lib/primer/forms/multi.rb +2 -0
  86. data/lib/primer/forms/radio_button_group.html.erb +14 -9
  87. data/lib/primer/forms/radio_button_group.rb +5 -0
  88. data/lib/primer/forms/{select_list.html.erb → select.html.erb} +0 -0
  89. data/lib/primer/forms/{select_list.rb → select.rb} +2 -2
  90. data/lib/primer/forms/spacing_wrapper.html.erb +1 -1
  91. data/lib/primer/forms/text_area.rb +1 -1
  92. data/lib/primer/forms/text_field.rb +5 -1
  93. data/lib/primer/forms/utils.rb +20 -0
  94. data/lib/primer/view_components/engine.rb +1 -1
  95. data/lib/primer/view_components/version.rb +1 -1
  96. data/lib/primer/yard/backend.rb +1 -15
  97. data/lib/primer/yard/component_manifest.rb +44 -25
  98. data/lib/primer/yard/component_ref.rb +40 -0
  99. data/lib/primer/yard/docs_helper.rb +16 -2
  100. data/lib/primer/yard/legacy_gatsby_backend.rb +9 -15
  101. data/lib/primer/yard/lookbook_docs_helper.rb +32 -0
  102. data/lib/primer/yard/lookbook_pages_backend.rb +194 -0
  103. data/lib/primer/yard/registry.rb +6 -21
  104. data/lib/primer/yard/renders_many_handler.rb +1 -1
  105. data/lib/primer/yard/renders_one_handler.rb +1 -1
  106. data/lib/primer/yard.rb +14 -0
  107. data/lib/tasks/docs.rake +26 -13
  108. data/previews/pages/forms/01_introduction.md.erb +44 -0
  109. data/previews/pages/forms/02_getting_started.md.erb +125 -0
  110. data/previews/pages/forms/03_caption_templates.md.erb +30 -0
  111. data/previews/pages/forms/04_after_content.md.erb +39 -0
  112. data/previews/pages/forms/05_groups_layouts.md.erb +22 -0
  113. data/previews/pages/forms/06_miscellaneous_inputs.md.erb +43 -0
  114. data/previews/pages/forms/07_toggle_switch_forms.md.erb +58 -0
  115. data/previews/pages/forms/08_validations.md.erb +28 -0
  116. data/previews/pages/forms/09_compound_forms.md.erb +97 -0
  117. data/previews/primer/alpha/check_box_group_preview.rb +89 -0
  118. data/previews/primer/alpha/check_box_preview.rb +62 -0
  119. data/previews/primer/alpha/form_control_preview/playground.html.erb +9 -0
  120. data/previews/primer/alpha/form_control_preview.rb +106 -0
  121. data/previews/primer/alpha/multi_input_preview/playground.html.erb +41 -0
  122. data/previews/primer/alpha/multi_input_preview.rb +80 -0
  123. data/previews/primer/alpha/radio_button_group_preview.rb +83 -0
  124. data/previews/primer/alpha/radio_button_preview.rb +62 -0
  125. data/previews/primer/alpha/select_preview.rb +130 -0
  126. data/previews/primer/alpha/text_area_preview.rb +87 -0
  127. data/previews/primer/alpha/text_field_preview.rb +10 -1
  128. data/previews/primer/forms/forms_preview/example_toggle_switch_form.html.erb +2 -2
  129. data/previews/primer/forms/forms_preview/{select_list_form.html.erb → select_form.html.erb} +1 -1
  130. data/previews/primer/forms/forms_preview.rb +3 -1
  131. data/static/arguments.json +1358 -1328
  132. data/static/audited_at.json +10 -0
  133. data/static/constants.json +20 -0
  134. data/static/previews.json +218 -40
  135. data/static/statuses.json +10 -0
  136. 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
- {"name":"alpha/layout","selectors":[".Layout",".Layout .Layout-divider",".Layout .Layout-main",".Layout .Layout-sidebar",".Layout.Layout--sidebarPosition-flowRow-start .Layout-sidebar",".Layout.Layout--sidebarPosition-flowRow-end .Layout-sidebar",".Layout.Layout--sidebarPosition-flowRow-start .Layout-main",".Layout.Layout--sidebarPosition-flowRow-end .Layout-main",".Layout.Layout--sidebarPosition-flowRow-none .Layout-sidebar",".Layout.Layout--divided",".Layout.Layout--divided .Layout-divider",".Layout.Layout--divided .Layout-divider.Layout-divider--flowRow-hidden",".Layout.Layout--divided .Layout-divider.Layout-divider--flowRow-shallow",".Layout.Layout--divided .Layout-main",".Layout.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-sidebar",".Layout.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-main",".Layout.Layout--flowRow-until-md",".Layout.Layout--flowRow-until-md .Layout-divider",".Layout.Layout--flowRow-until-md .Layout-main",".Layout.Layout--flowRow-until-md .Layout-sidebar",".Layout.Layout--flowRow-until-md.Layout--sidebarPosition-flowRow-start .Layout-sidebar",".Layout.Layout--flowRow-until-md.Layout--sidebarPosition-flowRow-end .Layout-sidebar",".Layout.Layout--flowRow-until-md.Layout--sidebarPosition-flowRow-start .Layout-main",".Layout.Layout--flowRow-until-md.Layout--sidebarPosition-flowRow-end .Layout-main",".Layout.Layout--flowRow-until-md.Layout--sidebarPosition-flowRow-none .Layout-sidebar",".Layout.Layout--flowRow-until-md.Layout--divided",".Layout.Layout--flowRow-until-md.Layout--divided .Layout-divider",".Layout.Layout--flowRow-until-md.Layout--divided .Layout-divider.Layout-divider--flowRow-hidden",".Layout.Layout--flowRow-until-md.Layout--divided .Layout-divider.Layout-divider--flowRow-shallow",".Layout.Layout--flowRow-until-md.Layout--divided .Layout-main",".Layout.Layout--flowRow-until-md.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-sidebar",".Layout.Layout--flowRow-until-md.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-main",".Layout.Layout--flowRow-until-lg",".Layout.Layout--flowRow-until-lg .Layout-divider",".Layout.Layout--flowRow-until-lg .Layout-main",".Layout.Layout--flowRow-until-lg .Layout-sidebar",".Layout.Layout--flowRow-until-lg.Layout--sidebarPosition-flowRow-start .Layout-sidebar",".Layout.Layout--flowRow-until-lg.Layout--sidebarPosition-flowRow-end .Layout-sidebar",".Layout.Layout--flowRow-until-lg.Layout--sidebarPosition-flowRow-start .Layout-main",".Layout.Layout--flowRow-until-lg.Layout--sidebarPosition-flowRow-end .Layout-main",".Layout.Layout--flowRow-until-lg.Layout--sidebarPosition-flowRow-none .Layout-sidebar",".Layout.Layout--flowRow-until-lg.Layout--divided",".Layout.Layout--flowRow-until-lg.Layout--divided .Layout-divider",".Layout.Layout--flowRow-until-lg.Layout--divided .Layout-divider.Layout-divider--flowRow-hidden",".Layout.Layout--flowRow-until-lg.Layout--divided .Layout-divider.Layout-divider--flowRow-shallow",".Layout.Layout--flowRow-until-lg.Layout--divided .Layout-main",".Layout.Layout--flowRow-until-lg.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-sidebar",".Layout.Layout--flowRow-until-lg.Layout--divided.Layout--sidebarPosition-flowRow-end .Layout-main",".Layout.Layout--gutter-none",".Layout.Layout--gutter-condensed",".Layout.Layout--gutter-spacious",".Layout.Layout--sidebar-narrow",".Layout.Layout--sidebar-wide",".Layout.Layout--sidebarPosition-start .Layout-sidebar",".Layout.Layout--sidebarPosition-start .Layout-main",".Layout.Layout--sidebarPosition-end",".Layout.Layout--sidebarPosition-end .Layout-main",".Layout.Layout--sidebarPosition-end .Layout-sidebar",".Layout.Layout--divided.Layout--sidebarPosition-end .Layout-sidebar",".Layout.Layout--divided.Layout--sidebarPosition-end .Layout-main",".Layout-divider",".Layout-sidebar",".Layout-main",".Layout-main .Layout-main-centered-lg",".Layout-main .Layout-main-centered-md",".Layout-main .Layout-main-centered-xl",".Layout-main .Layout-main-centered-lg>.container-lg",".Layout-main .Layout-main-centered-lg>.container-md",".Layout-main .Layout-main-centered-lg>.container-xl",".Layout-main .Layout-main-centered-md>.container-lg",".Layout-main .Layout-main-centered-md>.container-md",".Layout-main .Layout-main-centered-md>.container-xl",".Layout-main .Layout-main-centered-xl>.container-lg",".Layout-main .Layout-main-centered-xl>.container-md",".Layout-main .Layout-main-centered-xl>.container-xl"]}
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
- {"name":"alpha/menu","selectors":[".menu",".menu-item",".menu-item:first-child",".menu-item:first-child:before",".menu-item:last-child",".menu-item:last-child:before",".menu-item:hover",".menu-item:active",".menu-item.selected",".menu-item[aria-current]:not([aria-current=false])",".menu-item[aria-selected=true]",".menu-item.selected:before",".menu-item[aria-current]:not([aria-current=false]):before",".menu-item[aria-selected=true]:before",".menu-item .octicon",".menu-item .Counter",".menu-item .menu-warning",".menu-item .avatar",".menu-item.alert .Counter",".menu-heading",".menu-heading:hover",".menu-heading:first-child",".menu-heading:last-child"]}
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
@@ -119,6 +119,10 @@ module Primer
119
119
  def current_page?(url)
120
120
  helpers.current_page?(url)
121
121
  end
122
+
123
+ def list_class
124
+ Primer::Alpha::NavList
125
+ end
122
126
  end
123
127
  end
124
128
  end
@@ -38,7 +38,7 @@ module Primer
38
38
 
39
39
  # @private
40
40
  def self.custom_element_name
41
- "nav-list"
41
+ Primer::Alpha::NavList.custom_element_name
42
42
  end
43
43
 
44
44
  # @param selected_item_id [Symbol] The ID of the currently selected item. Used internally.
@@ -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.parseHTML(document, html);
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
- parseHTML(document, html) {
113
- const template = document.createElement('template');
114
- // eslint-disable-next-line github/no-inner-html
115
- template.innerHTML = html;
116
- return document.importNode(template.content, true);
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.parseHTML(document, html)
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
- private parseHTML(document: Document, html: string): DocumentFragment {
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 {