openproject-primer_view_components 0.84.4 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/app/assets/javascripts/components/primer/open_project/sub_header_element.d.ts +1 -1
  4. data/app/assets/javascripts/primer_view_components.js +1 -1
  5. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  6. data/app/assets/styles/primer_view_components.css +1 -1
  7. data/app/assets/styles/primer_view_components.css.map +1 -1
  8. data/app/components/primer/alpha/action_menu/action_menu_element.js +4 -0
  9. data/app/components/primer/alpha/action_menu/action_menu_element.ts +3 -0
  10. data/app/components/primer/alpha/select_panel_element.js +2 -2
  11. data/app/components/primer/alpha/select_panel_element.ts +2 -2
  12. data/app/components/primer/open_project/sub_header.css +1 -1
  13. data/app/components/primer/open_project/sub_header.css.json +2 -1
  14. data/app/components/primer/open_project/sub_header.css.map +1 -1
  15. data/app/components/primer/open_project/sub_header.html.erb +11 -8
  16. data/app/components/primer/open_project/sub_header.pcss +14 -7
  17. data/app/components/primer/open_project/sub_header.rb +46 -25
  18. data/app/components/primer/open_project/sub_header_element.d.ts +1 -1
  19. data/app/components/primer/open_project/sub_header_element.js +6 -6
  20. data/app/components/primer/open_project/sub_header_element.ts +5 -10
  21. data/lib/primer/view_components/version.rb +2 -2
  22. data/previews/primer/alpha/action_menu_preview/multiple_select_form.html.erb +8 -8
  23. data/previews/primer/alpha/action_menu_preview/opens_dialog.html.erb +4 -12
  24. data/previews/primer/alpha/action_menu_preview/single_select_form_items.html.erb +4 -34
  25. data/previews/primer/alpha/action_menu_preview/with_actions.html.erb +8 -15
  26. data/previews/primer/alpha/action_menu_preview.rb +1 -0
  27. data/previews/primer/alpha/select_panel_preview.rb +0 -27
  28. data/previews/primer/alpha/text_area_preview.rb +0 -1
  29. data/previews/primer/alpha/text_field_preview.rb +0 -1
  30. data/previews/primer/open_project/inline_message_preview/playground.html.erb +1 -3
  31. data/previews/primer/open_project/inline_message_preview.rb +1 -0
  32. data/previews/primer/open_project/sub_header_preview/quick_filters.html.erb +47 -0
  33. data/previews/primer/open_project/sub_header_preview.rb +23 -1
  34. data/static/arguments.json +6 -0
  35. data/static/constants.json +1 -1
  36. data/static/info_arch.json +66 -29
  37. data/static/previews.json +55 -29
  38. metadata +3 -3
  39. data/previews/primer/open_project/inline_message_preview/default.html.erb +0 -5
@@ -19,6 +19,7 @@ var _ActionMenuElement_instances, _a, _ActionMenuElement_abortController, _Actio
19
19
  var ActionMenuElement_1;
20
20
  import { controller, target } from '@github/catalyst';
21
21
  import '@oddbird/popover-polyfill';
22
+ import AnchoredPositionElement from '../../anchored_position';
22
23
  import { observeMutationsUntilConditionMet } from '../../utils';
23
24
  import { ActionMenuFocusZoneStack } from './action_menu_focus_zone_stack';
24
25
  import { ClipboardCopyElement } from '@github/clipboard-copy-element';
@@ -365,6 +366,9 @@ _ActionMenuElement_handleItemKeyboardEvent = function _ActionMenuElement_handleI
365
366
  };
366
367
  _ActionMenuElement_handleToggleEvent = function _ActionMenuElement_handleToggleEvent(event) {
367
368
  const subMenu = event.target;
369
+ // Ignore toggle events from non-anchored-position elements (e.g. tooltips inside the menu)
370
+ if (!(event.target instanceof AnchoredPositionElement))
371
+ return;
368
372
  if (event.newState === 'open') {
369
373
  // allow tabbing away from primary menu, but trap focus in sub-menus
370
374
  const isPrimaryMenu = subMenu === this.overlay;
@@ -346,6 +346,9 @@ export class ActionMenuElement extends HTMLElement {
346
346
  #handleToggleEvent(event: ToggleEvent) {
347
347
  const subMenu = event.target as AnchoredPositionElement
348
348
 
349
+ // Ignore toggle events from non-anchored-position elements (e.g. tooltips inside the menu)
350
+ if (!(event.target instanceof AnchoredPositionElement)) return
351
+
349
352
  if (event.newState === 'open') {
350
353
  // allow tabbing away from primary menu, but trap focus in sub-menus
351
354
  const isPrimaryMenu = subMenu === this.overlay
@@ -154,8 +154,8 @@ let SelectPanelElement = class SelectPanelElement extends HTMLElement {
154
154
  side: this.side,
155
155
  anchorOffset: 4,
156
156
  });
157
- this.dialog.style.top = `${top}px`;
158
- this.dialog.style.left = `${left}px`;
157
+ this.dialog.style.top = `${Math.round(top)}px`;
158
+ this.dialog.style.left = `${Math.round(left)}px`;
159
159
  this.dialog.style.bottom = 'auto';
160
160
  this.dialog.style.right = 'auto';
161
161
  }
@@ -176,8 +176,8 @@ export class SelectPanelElement extends HTMLElement {
176
176
  side: this.side,
177
177
  anchorOffset: 4,
178
178
  })
179
- this.dialog.style.top = `${top}px`
180
- this.dialog.style.left = `${left}px`
179
+ this.dialog.style.top = `${Math.round(top)}px`
180
+ this.dialog.style.left = `${Math.round(left)}px`
181
181
  this.dialog.style.bottom = 'auto'
182
182
  this.dialog.style.right = 'auto'
183
183
  }
@@ -1 +1 @@
1
- .SubHeader{align-items:center;display:grid;grid-template-areas:"left middle right" "bottom bottom bottom";grid-template-columns:auto 1fr auto;margin-bottom:var(--base-size-16)}.SubHeader--expandedSearch{grid-template-areas:"left left left" "bottom bottom bottom"}.SubHeader-rightPane{align-items:center;column-gap:12px;display:flex;grid-area:right}.SubHeader-middlePane{grid-area:middle;text-align:center}.SubHeader-bottomPane{grid-area:bottom}.SubHeader-leftPane{align-items:center;display:flex;grid-area:left;width:100%}:is(.SubHeader-leftPane [class*=FormControl-input-width--]):not(.FormControl-input-width--auto){width:100vw}.SubHeader-filterContainer{display:flex;flex-basis:max-content;gap:8px;width:100%}.SubHeader-filterInput_hiddenClearButton+.FormControl-input-trailingAction{display:none}@media (max-width:543.98px){.SubHeader{grid-template-areas:"left right" "middle middle" "bottom bottom";grid-template-columns:1fr auto}.SubHeader--emptyLeftPane{grid-template-areas:"middle middle right" "bottom bottom bottom";grid-template-columns:auto 1fr auto}.SubHeader--emptyLeftPane .SubHeader-middlePane{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.SubHeader-middlePane{text-align:left}.SubHeader-middlePane:has(>*){margin-top:var(--stack-gap-normal)}}
1
+ .SubHeader{align-items:baseline;display:grid;grid-template-areas:"left middle right" "bottom bottom bottom";grid-template-columns:auto 1fr auto;margin-bottom:var(--base-size-16)}.SubHeader-rightPane{align-items:center;column-gap:12px;display:flex;grid-area:right}.SubHeader-middlePane{grid-area:middle;text-align:center;white-space:nowrap}.SubHeader-bottomPane{grid-area:bottom}.SubHeader-leftPane{align-items:center;display:flex;flex-wrap:wrap;grid-area:left;row-gap:var(--base-size-16);width:100%}:is(.SubHeader-leftPane [class*=FormControl-input-width--]):not(.FormControl-input-width--auto){width:100vw}.SubHeader-filterContainer{display:flex;flex-basis:max-content;gap:8px;width:100%}.SubHeader-filterInput_hiddenClearButton+.FormControl-input-trailingAction{display:none}@media (max-width:767.98px){.SubHeader{grid-template-areas:"left right" "middle middle" "bottom bottom";grid-template-columns:1fr auto}.SubHeader--expandedSearch{grid-template-areas:"left left" "middle middle" "bottom bottom"}.SubHeader--expandedSearch .SubHeader-hiddenOnExpand{display:none!important}.SubHeader--emptyLeftPane{grid-template-areas:"middle middle right" "bottom bottom bottom";grid-template-columns:auto 1fr auto}.SubHeader--emptyLeftPane .SubHeader-middlePane{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.SubHeader-middlePane{text-align:left}.SubHeader-middlePane:has(>*){margin-top:var(--base-size-16)}}
@@ -2,7 +2,6 @@
2
2
  "name": "open_project/sub_header",
3
3
  "selectors": [
4
4
  ".SubHeader",
5
- ".SubHeader--expandedSearch",
6
5
  ".SubHeader-rightPane",
7
6
  ".SubHeader-middlePane",
8
7
  ".SubHeader-bottomPane",
@@ -10,6 +9,8 @@
10
9
  ":is(.SubHeader-leftPane [class*=FormControl-input-width--]):not(.FormControl-input-width--auto)",
11
10
  ".SubHeader-filterContainer",
12
11
  ".SubHeader-filterInput_hiddenClearButton+.FormControl-input-trailingAction",
12
+ ".SubHeader--expandedSearch",
13
+ ".SubHeader--expandedSearch .SubHeader-hiddenOnExpand",
13
14
  ".SubHeader--emptyLeftPane",
14
15
  ".SubHeader--emptyLeftPane .SubHeader-middlePane",
15
16
  ".SubHeader-middlePane:has(>*)"
@@ -1 +1 @@
1
- {"version":3,"sources":["sub_header.pcss"],"names":[],"mappings":"AAEA,WAII,kBAAmB,CAHnB,YAAa,CACb,8DAA+D,CAC/D,mCAAoC,CAEpC,iCACJ,CAEA,2BACI,2DACJ,CAEA,qBAGI,kBAAmB,CACnB,eAAgB,CAFhB,YAAa,CADb,eAIJ,CAEA,sBACI,gBAAiB,CACjB,iBACJ,CAEA,sBACI,gBACJ,CAEA,oBAGI,kBAAmB,CADnB,YAAa,CADb,cAAe,CAGf,UASJ,CAJQ,gGACI,WACJ,CAIR,2BACI,YAAa,CACb,sBAAuB,CAEvB,OAAQ,CADR,UAEJ,CAEA,2EACE,YACF,CAEA,4BACI,WACI,gEAAiE,CACjE,8BACJ,CAEA,0BACI,gEAAiE,CACjE,mCACJ,CAEA,gDAGI,eAAgB,CADhB,sBAAuB,CADvB,kBAGJ,CAEA,sBACI,eACJ,CAEA,8BACI,kCACJ,CACJ","file":"sub_header.css","sourcesContent":["/* CSS for SubHeader */\n\n.SubHeader {\n display: grid;\n grid-template-areas: \"left middle right\" \"bottom bottom bottom\";\n grid-template-columns: auto 1fr auto;\n align-items: center;\n margin-bottom: var(--base-size-16);\n}\n\n.SubHeader--expandedSearch {\n grid-template-areas: \"left left left\" \"bottom bottom bottom\";\n}\n\n.SubHeader-rightPane {\n grid-area: right;\n display: flex;\n align-items: center;\n column-gap: 12px;\n}\n\n.SubHeader-middlePane {\n grid-area: middle;\n text-align: center;\n}\n\n.SubHeader-bottomPane {\n grid-area: bottom;\n}\n\n.SubHeader-leftPane {\n grid-area: left;\n display: flex;\n align-items: center;\n width: 100%;\n\n /* Since the container is not full width (due to the grid around it)\n we want it to grow, and then be limited by the max-width of the \"FormControl-input-width--xy\" class */\n & [class*='FormControl-input-width--'] {\n &:not(.FormControl-input-width--auto) {\n width: 100vw;\n }\n }\n}\n\n.SubHeader-filterContainer {\n display: flex;\n flex-basis: max-content;\n width: 100%;\n gap: 8px;\n}\n\n.SubHeader-filterInput_hiddenClearButton + .FormControl-input-trailingAction {\n display: none;\n}\n\n@media (max-width: 543.98px) {\n .SubHeader {\n grid-template-areas: \"left right\" \"middle middle\" \"bottom bottom\";\n grid-template-columns: 1fr auto;\n }\n\n .SubHeader--emptyLeftPane {\n grid-template-areas: \"middle middle right\" \"bottom bottom bottom\";\n grid-template-columns: auto 1fr auto;\n }\n\n .SubHeader--emptyLeftPane .SubHeader-middlePane {\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n }\n\n .SubHeader-middlePane {\n text-align: left;\n }\n\n .SubHeader-middlePane:has(> *) {\n margin-top: var(--stack-gap-normal);\n }\n}\n"]}
1
+ {"version":3,"sources":["sub_header.pcss"],"names":[],"mappings":"AAEA,WAII,oBAAqB,CAHrB,YAAa,CACb,8DAA+D,CAC/D,mCAAoC,CAEpC,iCACJ,CAEA,qBAGI,kBAAmB,CACnB,eAAgB,CAFhB,YAAa,CADb,eAIJ,CAEA,sBACI,gBAAiB,CACjB,iBAAkB,CAClB,kBACJ,CAEA,sBACI,gBACJ,CAEA,oBAGI,kBAAmB,CADnB,YAAa,CAEb,cAAe,CAHf,cAAe,CAIf,2BAA4B,CAC5B,UASJ,CAJQ,gGACI,WACJ,CAIR,2BACI,YAAa,CACb,sBAAuB,CAEvB,OAAQ,CADR,UAEJ,CAEA,2EACE,YACF,CAEA,4BACI,WACI,gEAAiE,CACjE,8BACJ,CAEA,2BACI,+DACJ,CAEA,qDACI,sBACJ,CAEA,0BACI,gEAAiE,CACjE,mCACJ,CAEA,gDAGI,eAAgB,CADhB,sBAAuB,CADvB,kBAGJ,CAEA,sBACI,eACJ,CAEA,8BACI,8BACJ,CACJ","file":"sub_header.css","sourcesContent":["/* CSS for SubHeader */\n\n.SubHeader {\n display: grid;\n grid-template-areas: \"left middle right\" \"bottom bottom bottom\";\n grid-template-columns: auto 1fr auto;\n align-items: baseline;\n margin-bottom: var(--base-size-16);\n}\n\n.SubHeader-rightPane {\n grid-area: right;\n display: flex;\n align-items: center;\n column-gap: 12px;\n}\n\n.SubHeader-middlePane {\n grid-area: middle;\n text-align: center;\n white-space: nowrap;\n}\n\n.SubHeader-bottomPane {\n grid-area: bottom;\n}\n\n.SubHeader-leftPane {\n grid-area: left;\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n row-gap: var(--base-size-16);\n width: 100%;\n\n /* Since the container is not full width (due to the grid around it)\n we want it to grow, and then be limited by the max-width of the \"FormControl-input-width--xy\" class */\n & [class*='FormControl-input-width--'] {\n &:not(.FormControl-input-width--auto) {\n width: 100vw;\n }\n }\n}\n\n.SubHeader-filterContainer {\n display: flex;\n flex-basis: max-content;\n width: 100%;\n gap: 8px;\n}\n\n.SubHeader-filterInput_hiddenClearButton + .FormControl-input-trailingAction {\n display: none;\n}\n\n@media (max-width: 767.98px) {\n .SubHeader {\n grid-template-areas: \"left right\" \"middle middle\" \"bottom bottom\";\n grid-template-columns: 1fr auto;\n }\n\n .SubHeader--expandedSearch {\n grid-template-areas: \"left left\" \"middle middle\" \"bottom bottom\";\n }\n\n .SubHeader--expandedSearch .SubHeader-hiddenOnExpand {\n display: none !important;\n }\n\n .SubHeader--emptyLeftPane {\n grid-template-areas: \"middle middle right\" \"bottom bottom bottom\";\n grid-template-columns: auto 1fr auto;\n }\n\n .SubHeader--emptyLeftPane .SubHeader-middlePane {\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n }\n\n .SubHeader-middlePane {\n text-align: left;\n }\n\n .SubHeader-middlePane:has(> *) {\n margin-top: var(--base-size-16);\n }\n}\n"]}
@@ -2,16 +2,19 @@
2
2
  <div class="SubHeader-leftPane">
3
3
  <%= render @filter_container do %>
4
4
  <%= filter_input %>
5
- <%= render @mobile_filter_cancel do
6
- I18n.t("button_cancel")
7
- end if @mobile_filter_cancel.present? %>
5
+ <%= render @collapsed_filter_cancel if @collapsed_filter_cancel.present? %>
8
6
  <% end if filter_input.present? %>
9
7
 
10
- <%= render @mobile_filter_trigger if @mobile_filter_trigger.present? %>
8
+ <%= render @collapsed_filter_trigger if @collapsed_filter_trigger.present? %>
11
9
 
12
- <%= render(@mobile_filter_button) if @mobile_filter_button.present? %>
10
+ <%= render(Primer::BaseComponent.new(tag: :div, display: :flex)) do %>
11
+ <% quick_filters.each do |quick_filter| %>
12
+ <%= quick_filter %>
13
+ <% end %>
13
14
 
14
- <%= filter_button %>
15
+ <%= render(@mobile_filter_button) if @mobile_filter_button.present? %>
16
+ <%= filter_button %>
17
+ <% end %>
15
18
 
16
19
  <%= segmented_control %>
17
20
 
@@ -22,11 +25,11 @@
22
25
  <% end %>
23
26
  </div>
24
27
 
25
- <div class="SubHeader-middlePane" data-targets="<%= HIDDEN_FILTER_TARGET_SELECTOR %>">
28
+ <div class="SubHeader-middlePane SubHeader-hiddenOnExpand">
26
29
  <%= text %>
27
30
  </div>
28
31
 
29
- <div class="SubHeader-rightPane" data-targets="<%= HIDDEN_FILTER_TARGET_SELECTOR %>">
32
+ <div class="SubHeader-rightPane SubHeader-hiddenOnExpand">
30
33
  <% actions.each do |action| %>
31
34
  <%= action %>
32
35
  <% end %>
@@ -4,14 +4,10 @@
4
4
  display: grid;
5
5
  grid-template-areas: "left middle right" "bottom bottom bottom";
6
6
  grid-template-columns: auto 1fr auto;
7
- align-items: center;
7
+ align-items: baseline;
8
8
  margin-bottom: var(--base-size-16);
9
9
  }
10
10
 
11
- .SubHeader--expandedSearch {
12
- grid-template-areas: "left left left" "bottom bottom bottom";
13
- }
14
-
15
11
  .SubHeader-rightPane {
16
12
  grid-area: right;
17
13
  display: flex;
@@ -22,6 +18,7 @@
22
18
  .SubHeader-middlePane {
23
19
  grid-area: middle;
24
20
  text-align: center;
21
+ white-space: nowrap;
25
22
  }
26
23
 
27
24
  .SubHeader-bottomPane {
@@ -32,6 +29,8 @@
32
29
  grid-area: left;
33
30
  display: flex;
34
31
  align-items: center;
32
+ flex-wrap: wrap;
33
+ row-gap: var(--base-size-16);
35
34
  width: 100%;
36
35
 
37
36
  /* Since the container is not full width (due to the grid around it)
@@ -54,12 +53,20 @@
54
53
  display: none;
55
54
  }
56
55
 
57
- @media (max-width: 543.98px) {
56
+ @media (max-width: 767.98px) {
58
57
  .SubHeader {
59
58
  grid-template-areas: "left right" "middle middle" "bottom bottom";
60
59
  grid-template-columns: 1fr auto;
61
60
  }
62
61
 
62
+ .SubHeader--expandedSearch {
63
+ grid-template-areas: "left left" "middle middle" "bottom bottom";
64
+ }
65
+
66
+ .SubHeader--expandedSearch .SubHeader-hiddenOnExpand {
67
+ display: none !important;
68
+ }
69
+
63
70
  .SubHeader--emptyLeftPane {
64
71
  grid-template-areas: "middle middle right" "bottom bottom bottom";
65
72
  grid-template-columns: auto 1fr auto;
@@ -76,6 +83,6 @@
76
83
  }
77
84
 
78
85
  .SubHeader-middlePane:has(> *) {
79
- margin-top: var(--stack-gap-normal);
86
+ margin-top: var(--base-size-16);
80
87
  }
81
88
  }
@@ -7,8 +7,8 @@ module Primer
7
7
  class SubHeader < Primer::Component
8
8
  status :open_project
9
9
 
10
- HIDDEN_FILTER_TARGET_SELECTOR = "sub-header.hiddenItemsOnExpandedFilter"
11
10
  SHOWN_FILTER_TARGET_SELECTOR = "sub-header.shownItemsOnExpandedFilter"
11
+ FILTER_EXPAND_BUTTON_TARGET_SELECTOR = "sub-header.filterExpandButton"
12
12
 
13
13
  MOBILE_ACTIONS_DISPLAY = [:flex, :none].freeze
14
14
  DESKTOP_ACTIONS_DISPLAY = [:none, :flex].freeze
@@ -101,18 +101,23 @@ module Primer
101
101
  system_arguments[:data][:action] += " input:sub-header#toggleFilterInputClearButton focus:sub-header#toggleFilterInputClearButton"
102
102
  end
103
103
 
104
- @mobile_filter_trigger = Primer::Beta::IconButton.new(icon: system_arguments[:leading_visual][:icon],
105
- display: [:inline_flex, :none],
106
- aria: { label: label },
107
- mr: 2,
108
- "data-action": "click:sub-header#expandFilterInput",
109
- "data-targets": HIDDEN_FILTER_TARGET_SELECTOR)
104
+ trigger_display = @collapsed_search ? :inline_flex : [:inline_flex, :none]
110
105
 
111
- @mobile_filter_cancel = Primer::Beta::Button.new(scheme: :invisible,
112
- display: :none,
113
- data: {
114
- targets: SHOWN_FILTER_TARGET_SELECTOR,
115
- action: "click:sub-header#collapseFilterInput"})
106
+ @collapsed_filter_trigger = Primer::Beta::IconButton.new(icon: system_arguments[:leading_visual][:icon],
107
+ display: trigger_display,
108
+ aria: { label: label },
109
+ mr: 2,
110
+ "data-action": "click:sub-header#expandFilterInput",
111
+ "data-targets": FILTER_EXPAND_BUTTON_TARGET_SELECTOR)
112
+
113
+ @collapsed_filter_cancel = Primer::Beta::IconButton.new(icon: :x,
114
+ "aria-label": I18n.t(:button_cancel),
115
+ scheme: :invisible,
116
+ display: :none,
117
+ data: {
118
+ targets: SHOWN_FILTER_TARGET_SELECTOR,
119
+ action: "click:sub-header#collapseFilterInput"
120
+ })
116
121
 
117
122
 
118
123
  Primer::Alpha::TextField.new(name: name, label: label, **system_arguments)
@@ -165,6 +170,18 @@ module Primer
165
170
  }
166
171
  }
167
172
 
173
+ # Quick filters shown in the left pane next to the search bar (0–5 items).
174
+ # Hidden on mobile. Requires all_filters_button to be set when used.
175
+ # Supports ActionMenus, Buttons, IconButtons, SelectPanels, and SegmentedControls inside the block.
176
+ renders_many :quick_filters, lambda { |**kwargs|
177
+ deny_tag_argument(**kwargs)
178
+ kwargs[:tag] = :div
179
+ kwargs[:mr] ||= 2
180
+ kwargs[:display] = DESKTOP_ACTIONS_DISPLAY
181
+
182
+ Primer::BaseComponent.new(**kwargs)
183
+ }
184
+
168
185
  renders_one :segmented_control, lambda { |**system_arguments, &block|
169
186
  deny_tag_argument(**system_arguments)
170
187
  system_arguments[:mr] ||= 2
@@ -182,6 +199,7 @@ module Primer
182
199
 
183
200
  renders_one :text, lambda { |**system_arguments|
184
201
  system_arguments[:font_weight] ||= :bold
202
+ system_arguments[:mx] ||= 2
185
203
 
186
204
  Primer::Beta::Text.new(**system_arguments)
187
205
  }
@@ -195,16 +213,18 @@ module Primer
195
213
  Primer::BaseComponent.new(**system_arguments)
196
214
  }
197
215
 
198
-
216
+ # @param collapsed_search [Boolean] When true, the search bar starts collapsed as an icon button on all screen sizes. Clicking expands it.
199
217
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
200
- def initialize(**system_arguments)
218
+ def initialize(collapsed_search: false, **system_arguments)
219
+ @collapsed_search = collapsed_search
201
220
  @system_arguments = system_arguments
202
221
  @system_arguments[:tag] = :"sub-header"
203
222
 
223
+ filter_container_display = collapsed_search ? :none : DESKTOP_ACTIONS_DISPLAY
224
+
204
225
  @filter_container = Primer::BaseComponent.new(tag: :div,
205
226
  classes: "SubHeader-filterContainer",
206
- display: DESKTOP_ACTIONS_DISPLAY,
207
-
227
+ display: filter_container_display,
208
228
  mr: 2,
209
229
  data: { targets: SHOWN_FILTER_TARGET_SELECTOR })
210
230
 
@@ -215,21 +235,22 @@ module Primer
215
235
  end
216
236
 
217
237
  def before_render
238
+ if quick_filters.any? && filter_button.nil?
239
+ raise ArgumentError, "You must provide a filter_button when using quick_filters."
240
+ end
241
+
242
+ if quick_filters.size > 5
243
+ raise ArgumentError, "SubHeader supports a maximum of 5 quick_filters, got #{quick_filters.size}."
244
+ end
245
+
218
246
  @system_arguments[:classes] = class_names(
219
247
  @system_arguments[:classes],
220
- "SubHeader--emptyLeftPane" => !segmented_control? && !filter_button && !filter_input
248
+ "SubHeader--emptyLeftPane" => !segmented_control? && !filter_button && !filter_input && quick_filters.empty?
221
249
  )
222
250
  end
223
251
 
224
252
  def set_as_hidden_filter_target(system_arguments)
225
- system_arguments[:data] ||= {}
226
- system_arguments[:data] = merge_data(
227
- system_arguments, {
228
- data: {
229
- targets: HIDDEN_FILTER_TARGET_SELECTOR,
230
- }
231
- }
232
- )
253
+ system_arguments[:classes] = class_names(system_arguments[:classes], "SubHeader-hiddenOnExpand")
233
254
  system_arguments
234
255
  end
235
256
  end
@@ -1,7 +1,7 @@
1
1
  declare class SubHeaderElement extends HTMLElement {
2
2
  filterInput: HTMLInputElement;
3
- hiddenItemsOnExpandedFilter: HTMLElement[];
4
3
  shownItemsOnExpandedFilter: HTMLElement[];
4
+ filterExpandButton: HTMLElement[];
5
5
  connectedCallback(): void;
6
6
  setupFilterInputClearButton(): void;
7
7
  toggleFilterInputClearButton(): void;
@@ -23,17 +23,17 @@ let SubHeaderElement = class SubHeaderElement extends HTMLElement {
23
23
  }
24
24
  }
25
25
  expandFilterInput() {
26
- for (const item of this.hiddenItemsOnExpandedFilter) {
27
- item.classList.add('d-none');
28
- }
29
26
  for (const item of this.shownItemsOnExpandedFilter) {
30
27
  item.classList.remove('d-none');
31
28
  }
29
+ for (const item of this.filterExpandButton) {
30
+ item.classList.add('d-none');
31
+ }
32
32
  this.classList.add('SubHeader--expandedSearch');
33
33
  this.filterInput.focus();
34
34
  }
35
35
  collapseFilterInput() {
36
- for (const item of this.hiddenItemsOnExpandedFilter) {
36
+ for (const item of this.filterExpandButton) {
37
37
  item.classList.remove('d-none');
38
38
  }
39
39
  for (const item of this.shownItemsOnExpandedFilter) {
@@ -63,10 +63,10 @@ __decorate([
63
63
  ], SubHeaderElement.prototype, "filterInput", void 0);
64
64
  __decorate([
65
65
  targets
66
- ], SubHeaderElement.prototype, "hiddenItemsOnExpandedFilter", void 0);
66
+ ], SubHeaderElement.prototype, "shownItemsOnExpandedFilter", void 0);
67
67
  __decorate([
68
68
  targets
69
- ], SubHeaderElement.prototype, "shownItemsOnExpandedFilter", void 0);
69
+ ], SubHeaderElement.prototype, "filterExpandButton", void 0);
70
70
  SubHeaderElement = __decorate([
71
71
  controller
72
72
  ], SubHeaderElement);
@@ -3,8 +3,8 @@ import {controller, target, targets} from '@github/catalyst'
3
3
  @controller
4
4
  class SubHeaderElement extends HTMLElement {
5
5
  @target filterInput: HTMLInputElement
6
- @targets hiddenItemsOnExpandedFilter: HTMLElement[]
7
6
  @targets shownItemsOnExpandedFilter: HTMLElement[]
7
+ @targets filterExpandButton: HTMLElement[]
8
8
 
9
9
  connectedCallback() {
10
10
  this.setupFilterInputClearButton()
@@ -28,28 +28,23 @@ class SubHeaderElement extends HTMLElement {
28
28
  }
29
29
 
30
30
  expandFilterInput() {
31
- for (const item of this.hiddenItemsOnExpandedFilter) {
32
- item.classList.add('d-none')
33
- }
34
-
35
31
  for (const item of this.shownItemsOnExpandedFilter) {
36
32
  item.classList.remove('d-none')
37
33
  }
38
-
34
+ for (const item of this.filterExpandButton) {
35
+ item.classList.add('d-none')
36
+ }
39
37
  this.classList.add('SubHeader--expandedSearch')
40
-
41
38
  this.filterInput.focus()
42
39
  }
43
40
 
44
41
  collapseFilterInput() {
45
- for (const item of this.hiddenItemsOnExpandedFilter) {
42
+ for (const item of this.filterExpandButton) {
46
43
  item.classList.remove('d-none')
47
44
  }
48
-
49
45
  for (const item of this.shownItemsOnExpandedFilter) {
50
46
  item.classList.add('d-none')
51
47
  }
52
-
53
48
  this.classList.remove('SubHeader--expandedSearch')
54
49
  }
55
50
 
@@ -5,8 +5,8 @@ module Primer
5
5
  module ViewComponents
6
6
  module VERSION
7
7
  MAJOR = 0
8
- MINOR = 84
9
- PATCH = 4
8
+ MINOR = 85
9
+ PATCH = 0
10
10
 
11
11
  STRING = [MAJOR, MINOR, PATCH].join(".")
12
12
  end
@@ -1,18 +1,18 @@
1
1
  <%= form_with(url: primer_view_components.action_menu_form_action_path(format: route_format)) do |f| %>
2
- <% content = -> (base) do %>
3
- <% base.with_item(label: "Fast forward", data: { value: "fast_forward" }) %>
4
- <% base.with_item(label: "Recursive", data: { value: "recursive" }) %>
5
- <% base.with_item(label: "Ours", data: { value: "ours" }, active: true) %>
6
- <% base.with_item(label: "Resolve") %>
7
- <% end %>
8
2
  <%= render(Primer::Alpha::ActionMenu.new(select_variant: :multiple, dynamic_label: true, dynamic_label_prefix: "Strategy", form_arguments: { builder: f, name: "foo" })) do |menu| %>
9
3
  <% menu.with_show_button { "Strategy" } %>
10
4
  <% if nest_in_sub_menu %>
11
5
  <% menu.with_sub_menu_item(label: "Sub-menu") do |sub_menu| %>
12
- <% content.call(sub_menu) %>
6
+ <% sub_menu.with_item(label: "Fast forward", data: { value: "fast_forward" }) %>
7
+ <% sub_menu.with_item(label: "Recursive", data: { value: "recursive" }) %>
8
+ <% sub_menu.with_item(label: "Ours", data: { value: "ours" }, active: true) %>
9
+ <% sub_menu.with_item(label: "Resolve") %>
13
10
  <% end %>
14
11
  <% else %>
15
- <% content.call(menu) %>
12
+ <% menu.with_item(label: "Fast forward", data: { value: "fast_forward" }) %>
13
+ <% menu.with_item(label: "Recursive", data: { value: "recursive" }) %>
14
+ <% menu.with_item(label: "Ours", data: { value: "ours" }, active: true) %>
15
+ <% menu.with_item(label: "Resolve") %>
16
16
  <% end %>
17
17
  <% end %>
18
18
  <hr>
@@ -1,21 +1,13 @@
1
1
  <%= render(Primer::Alpha::ActionMenu.new) do |component| %>
2
2
  <% component.with_show_button { "Menu" } %>
3
- <% contents = -> (base) do %>
4
- <% base.with_item(label: "Item", tag: :button, value: "") %>
5
- <% base.with_item(
6
- label: "Show dialog",
7
- tag: :button,
8
- content_arguments: { "data-show-dialog-id": "my-dialog" },
9
- value: "",
10
- scheme: :danger
11
- ) %>
12
- <% end %>
13
3
  <% if nest_in_sub_menu %>
14
4
  <% component.with_sub_menu_item(label: "Sub-menu") do |sub_menu_item| %>
15
- <% contents.call(sub_menu_item) %>
5
+ <% sub_menu_item.with_item(label: "Item", tag: :button, value: "") %>
6
+ <% sub_menu_item.with_item(label: "Show dialog", tag: :button, content_arguments: { "data-show-dialog-id": "my-dialog" }, value: "", scheme: :danger) %>
16
7
  <% end %>
17
8
  <% else %>
18
- <% contents.call(component) %>
9
+ <% component.with_item(label: "Item", tag: :button, value: "") %>
10
+ <% component.with_item(label: "Show dialog", tag: :button, content_arguments: { "data-show-dialog-id": "my-dialog" }, value: "", scheme: :danger) %>
19
11
  <% end %>
20
12
  <% end %>
21
13
 
@@ -1,42 +1,12 @@
1
- <% contents = -> (menu) do %>
2
- <% menu.with_item(
3
- label: "Repository",
4
- href: primer_view_components.action_menu_form_action_path(format: route_format),
5
- form_arguments: {
6
- method: :post,
7
- inputs: [{
8
- name: "query",
9
- value: "query"
10
- }, {
11
- name: "foo", # use "foo" here because that's what the controller expects
12
- value: "group-by-repository",
13
- }],
14
- }
15
- ) %>
16
- <% menu.with_item(
17
- label: "Date",
18
- href: primer_view_components.action_menu_form_action_path(format: route_format),
19
- form_arguments: {
20
- method: :post,
21
- inputs: [{
22
- name: "query",
23
- value: "query"
24
- }, {
25
- name: "foo", # use "foo" here because that's what the controller expects
26
- value: "sort-by-date"
27
- }]
28
- }
29
- ) %>
30
- <% end %>
31
-
32
1
  <%= render(Primer::Alpha::ActionMenu.new(select_variant: :single)) do |menu| %>
33
2
  <% menu.with_show_button { "Group By" } %>
34
-
35
3
  <% if nest_in_sub_menu %>
36
4
  <% menu.with_sub_menu_item(label: "Sub-menu") do |sub_menu_item| %>
37
- <% contents.call(sub_menu_item) %>
5
+ <% sub_menu_item.with_item(label: "Repository", href: primer_view_components.action_menu_form_action_path(format: route_format), form_arguments: { method: :post, inputs: [{ name: "query", value: "query" }, { name: "foo", value: "group-by-repository" }] }) %>
6
+ <% sub_menu_item.with_item(label: "Date", href: primer_view_components.action_menu_form_action_path(format: route_format), form_arguments: { method: :post, inputs: [{ name: "query", value: "query" }, { name: "foo", value: "sort-by-date" }] }) %>
38
7
  <% end %>
39
8
  <% else %>
40
- <% contents.call(menu) %>
9
+ <% menu.with_item(label: "Repository", href: primer_view_components.action_menu_form_action_path(format: route_format), form_arguments: { method: :post, inputs: [{ name: "query", value: "query" }, { name: "foo", value: "group-by-repository" }] }) %>
10
+ <% menu.with_item(label: "Date", href: primer_view_components.action_menu_form_action_path(format: route_format), form_arguments: { method: :post, inputs: [{ name: "query", value: "query" }, { name: "foo", value: "sort-by-date" }] }) %>
41
11
  <% end %>
42
12
  <% end %>
@@ -8,24 +8,17 @@
8
8
 
9
9
  <%= render(Primer::Alpha::ActionMenu.new) do |component| %>
10
10
  <% component.with_show_button { "Trigger" } %>
11
- <% contents = -> (base) do %>
12
- <% base.with_item(label: "Alert", tag: :button, id: "alert-item", disabled: disable_items) %>
13
- <% base.with_item(label: "Navigate", tag: :a, content_arguments: { href: primer_view_components.action_menu_landing_path }, disabled: disable_items) %>
14
- <% base.with_item(label: "Copy text", tag: :"clipboard-copy", content_arguments: { value: "Text to copy" }, disabled: disable_items) %>
15
- <% base.with_item(
16
- label: "Submit form",
17
- href: primer_view_components.action_menu_form_action_path(format: route_format),
18
- form_arguments: {
19
- name: "foo", value: "bar", method: :post
20
- },
21
- disabled: disable_items
22
- ) %>
23
- <% end %>
24
11
  <% if nest_in_sub_menu %>
25
12
  <% component.with_sub_menu_item(label: "Sub-menu") do |sub_menu_item| %>
26
- <% contents.call(sub_menu_item) %>
13
+ <% sub_menu_item.with_item(label: "Alert", tag: :button, id: "alert-item", disabled: disable_items) %>
14
+ <% sub_menu_item.with_item(label: "Navigate", tag: :a, content_arguments: { href: primer_view_components.action_menu_landing_path }, disabled: disable_items) %>
15
+ <% sub_menu_item.with_item(label: "Copy text", tag: :"clipboard-copy", content_arguments: { value: "Text to copy" }, disabled: disable_items) %>
16
+ <% sub_menu_item.with_item(label: "Submit form", href: primer_view_components.action_menu_form_action_path(format: route_format), form_arguments: { name: "foo", value: "bar", method: :post }, disabled: disable_items) %>
27
17
  <% end %>
28
18
  <% else %>
29
- <% contents.call(component) %>
19
+ <% component.with_item(label: "Alert", tag: :button, id: "alert-item", disabled: disable_items) %>
20
+ <% component.with_item(label: "Navigate", tag: :a, content_arguments: { href: primer_view_components.action_menu_landing_path }, disabled: disable_items) %>
21
+ <% component.with_item(label: "Copy text", tag: :"clipboard-copy", content_arguments: { value: "Text to copy" }, disabled: disable_items) %>
22
+ <% component.with_item(label: "Submit form", href: primer_view_components.action_menu_form_action_path(format: route_format), form_arguments: { name: "foo", value: "bar", method: :post }, disabled: disable_items) %>
30
23
  <% end %>
31
24
  <% end %>
@@ -155,6 +155,7 @@ module Primer
155
155
  render(Primer::Alpha::ActionMenu.new) do |menu|
156
156
  menu.with_show_button(icon: :star, "aria-label": "Menu")
157
157
  menu.with_item(label: "Does something")
158
+ menu.with_item(label: "Does something else")
158
159
  end
159
160
  end
160
161