openproject-primer_view_components 0.30.1 → 0.32.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/app/assets/javascripts/app/components/primer/open_project/sub_header_element.d.ts +13 -0
  4. data/app/assets/javascripts/app/components/primer/primer.d.ts +1 -0
  5. data/app/assets/javascripts/primer_view_components.js +1 -1
  6. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  7. data/app/assets/styles/primer_view_components.css +1 -1
  8. data/app/assets/styles/primer_view_components.css.map +1 -1
  9. data/app/components/primer/open_project/page_header.css +1 -1
  10. data/app/components/primer/open_project/page_header.css.json +3 -1
  11. data/app/components/primer/open_project/page_header.css.map +1 -1
  12. data/app/components/primer/open_project/page_header.html.erb +1 -0
  13. data/app/components/primer/open_project/page_header.pcss +10 -0
  14. data/app/components/primer/open_project/page_header.rb +14 -1
  15. data/app/components/primer/open_project/sub_header.css +1 -0
  16. data/app/components/primer/open_project/sub_header.css.json +11 -0
  17. data/app/components/primer/open_project/sub_header.css.map +1 -0
  18. data/app/components/primer/open_project/sub_header.html.erb +25 -0
  19. data/app/components/primer/open_project/sub_header.pcss +43 -0
  20. data/app/components/primer/open_project/sub_header.rb +141 -0
  21. data/app/components/primer/open_project/sub_header_element.d.ts +13 -0
  22. data/app/components/primer/open_project/sub_header_element.js +44 -0
  23. data/app/components/primer/open_project/sub_header_element.ts +45 -0
  24. data/app/components/primer/primer.d.ts +1 -0
  25. data/app/components/primer/primer.js +1 -0
  26. data/app/components/primer/primer.pcss +1 -0
  27. data/app/components/primer/primer.ts +1 -0
  28. data/lib/primer/view_components/version.rb +2 -2
  29. data/previews/primer/open_project/page_header_preview/playground.html.erb +7 -0
  30. data/previews/primer/open_project/page_header_preview.rb +22 -2
  31. data/previews/primer/open_project/sub_header_preview/action_menu_buttons.html.erb +15 -0
  32. data/previews/primer/open_project/sub_header_preview/bottom_pane.html.erb +12 -0
  33. data/previews/primer/open_project/sub_header_preview/button_group.html.erb +11 -0
  34. data/previews/primer/open_project/sub_header_preview/custom_filter_button.html.erb +8 -0
  35. data/previews/primer/open_project/sub_header_preview/dialog_buttons.html.erb +12 -0
  36. data/previews/primer/open_project/sub_header_preview.rb +88 -0
  37. data/static/arguments.json +16 -0
  38. data/static/audited_at.json +1 -0
  39. data/static/classes.json +24 -0
  40. data/static/constants.json +4 -0
  41. data/static/info_arch.json +196 -0
  42. data/static/previews.json +125 -0
  43. data/static/statuses.json +1 -0
  44. metadata +18 -2
@@ -1 +1 @@
1
- .PageHeader{border-bottom:var(--borderWidth-thin) solid var(--borderColor-muted);display:flex;flex-flow:column;margin-bottom:var(--stack-gap-normal);padding-bottom:var(--stack-padding-condensed)}.PageHeader-contextBar{margin-bottom:var(--base-size-8)}.PageHeader-contextBar,.PageHeader-titleBar{align-items:center;display:flex;flex-flow:row;justify-content:flex-end}.PageHeader-titleBar{margin-bottom:var(--space-xsmall)}.PageHeader-title{flex:1 1 auto;font-size:var(--text-title-size-medium);font-weight:var(--base-text-weight-normal)}.PageHeader-title--large{font-size:var(--text-title-size-large)}.PageHeader-description{color:var(--fgColor-muted);flex:1 100%;font-size:var(--text-body-size-medium)}.PageHeader-actions{align-items:center;display:flex;justify-content:flex-end}.PageHeader-breadcrumbs{display:block;width:100%}.PageHeader-leadingAction{margin-right:var(--base-size-4);margin-top:2px}.PageHeader-parentLink{flex:1 1 auto}
1
+ .PageHeader{border-bottom:var(--borderWidth-thin) solid var(--borderColor-muted);display:flex;flex-flow:column;margin-bottom:var(--stack-gap-normal);padding-bottom:var(--stack-padding-condensed)}.PageHeader--noBorder{border-bottom:none;padding-bottom:0}.PageHeader-contextBar{margin-bottom:var(--base-size-8)}.PageHeader-contextBar,.PageHeader-titleBar{align-items:center;display:flex;flex-flow:row;justify-content:flex-end}.PageHeader-titleBar{margin-bottom:var(--space-xsmall)}.PageHeader-title{flex:1 1 auto;font-size:var(--text-title-size-medium);font-weight:var(--base-text-weight-normal)}.PageHeader-title--large{font-size:var(--text-title-size-large)}.PageHeader-description{color:var(--fgColor-muted);flex:1 100%;font-size:var(--text-body-size-medium)}.PageHeader-actions{align-items:center;display:flex;justify-content:flex-end}.PageHeader-breadcrumbs{display:block;width:100%}.PageHeader-leadingAction{margin-right:var(--base-size-4);margin-top:2px}.PageHeader-parentLink{flex:1 1 auto}.PageHeader-tabNav{margin-bottom:0;margin-top:var(--stack-gap-normal)}
@@ -2,6 +2,7 @@
2
2
  "name": "open_project/page_header",
3
3
  "selectors": [
4
4
  ".PageHeader",
5
+ ".PageHeader--noBorder",
5
6
  ".PageHeader-contextBar",
6
7
  ".PageHeader-titleBar",
7
8
  ".PageHeader-title",
@@ -10,6 +11,7 @@
10
11
  ".PageHeader-actions",
11
12
  ".PageHeader-breadcrumbs",
12
13
  ".PageHeader-leadingAction",
13
- ".PageHeader-parentLink"
14
+ ".PageHeader-parentLink",
15
+ ".PageHeader-tabNav"
14
16
  ]
15
17
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["page_header.pcss"],"names":[],"mappings":"AAEA,YAIE,oEAAqE,CAHrE,YAAa,CAIb,gBAAiB,CAFjB,qCAAsC,CADtC,6CAIF,CAEA,uBAKE,gCACF,CAEA,4CAJE,kBAAmB,CAHnB,YAAa,CACb,aAAc,CACd,wBAWF,CANA,qBAKE,iCACF,CAEA,kBAGE,aAAc,CAFd,uCAAwC,CACxC,0CAEF,CAEA,yBACE,sCACF,CAGA,wBAEE,0BAA2B,CAC3B,WAAY,CAFZ,sCAGF,CAEA,oBAGE,kBAAmB,CADnB,YAAa,CADb,wBAGF,CAEA,wBACE,aAAc,CACd,UACF,CAEA,0BAEE,+BAAgC,CADhC,cAEF,CAEA,uBACE,aACF","file":"page_header.css","sourcesContent":["/* OP PageHeader */\n\n.PageHeader {\n display: flex;\n padding-bottom: var(--stack-padding-condensed);\n margin-bottom: var(--stack-gap-normal);\n border-bottom: var(--borderWidth-thin) solid var(--borderColor-muted);\n flex-flow: column;\n}\n\n.PageHeader-contextBar {\n display: flex;\n flex-flow: row;\n justify-content: flex-end;\n align-items: center;\n margin-bottom: var(--base-size-8);\n}\n\n.PageHeader-titleBar {\n display: flex;\n flex-flow: row;\n justify-content: flex-end;\n align-items: center; /* Keep back button vertically aligned. */\n margin-bottom: var(--space-xsmall);\n}\n\n.PageHeader-title {\n font-size: var(--text-title-size-medium);\n font-weight: var(--base-text-weight-normal);\n flex: 1 1 auto;\n}\n\n.PageHeader-title--large {\n font-size: var(--text-title-size-large);\n}\n\n/* One-liner of supporting text */\n.PageHeader-description {\n font-size: var(--text-body-size-medium);\n color: var(--fgColor-muted);\n flex: 1 100%;\n}\n\n.PageHeader-actions {\n justify-content: flex-end;\n display: flex;\n align-items: center;\n}\n\n.PageHeader-breadcrumbs {\n display: block;\n width: 100%;\n}\n\n.PageHeader-leadingAction {\n margin-top: 2px; /* to center align with label */\n margin-right: var(--base-size-4);\n}\n\n.PageHeader-parentLink {\n flex: 1 1 auto;\n}\n"]}
1
+ {"version":3,"sources":["page_header.pcss"],"names":[],"mappings":"AAEA,YAIE,oEAAqE,CAHrE,YAAa,CAIb,gBAAiB,CAFjB,qCAAsC,CADtC,6CAIF,CAEA,sBACE,kBAAmB,CACnB,gBACF,CAEA,uBAKE,gCACF,CAEA,4CAJE,kBAAmB,CAHnB,YAAa,CACb,aAAc,CACd,wBAWF,CANA,qBAKE,iCACF,CAEA,kBAGE,aAAc,CAFd,uCAAwC,CACxC,0CAEF,CAEA,yBACE,sCACF,CAGA,wBAEE,0BAA2B,CAC3B,WAAY,CAFZ,sCAGF,CAEA,oBAGE,kBAAmB,CADnB,YAAa,CADb,wBAGF,CAEA,wBACE,aAAc,CACd,UACF,CAEA,0BAEE,+BAAgC,CADhC,cAEF,CAEA,uBACE,aACF,CAEA,mBAEE,eAAgB,CADhB,kCAEF","file":"page_header.css","sourcesContent":["/* OP PageHeader */\n\n.PageHeader {\n display: flex;\n padding-bottom: var(--stack-padding-condensed);\n margin-bottom: var(--stack-gap-normal);\n border-bottom: var(--borderWidth-thin) solid var(--borderColor-muted);\n flex-flow: column;\n}\n\n.PageHeader--noBorder {\n border-bottom: none;\n padding-bottom: 0;\n}\n\n.PageHeader-contextBar {\n display: flex;\n flex-flow: row;\n justify-content: flex-end;\n align-items: center;\n margin-bottom: var(--base-size-8);\n}\n\n.PageHeader-titleBar {\n display: flex;\n flex-flow: row;\n justify-content: flex-end;\n align-items: center; /* Keep back button vertically aligned. */\n margin-bottom: var(--space-xsmall);\n}\n\n.PageHeader-title {\n font-size: var(--text-title-size-medium);\n font-weight: var(--base-text-weight-normal);\n flex: 1 1 auto;\n}\n\n.PageHeader-title--large {\n font-size: var(--text-title-size-large);\n}\n\n/* One-liner of supporting text */\n.PageHeader-description {\n font-size: var(--text-body-size-medium);\n color: var(--fgColor-muted);\n flex: 1 100%;\n}\n\n.PageHeader-actions {\n justify-content: flex-end;\n display: flex;\n align-items: center;\n}\n\n.PageHeader-breadcrumbs {\n display: block;\n width: 100%;\n}\n\n.PageHeader-leadingAction {\n margin-top: 2px; /* to center align with label */\n margin-right: var(--base-size-4);\n}\n\n.PageHeader-parentLink {\n flex: 1 1 auto;\n}\n\n.PageHeader-tabNav {\n margin-top: var(--stack-gap-normal);\n margin-bottom: 0;\n}\n"]}
@@ -27,4 +27,5 @@
27
27
  </div>
28
28
 
29
29
  <%= description %>
30
+ <%= tab_nav %>
30
31
  <% end %>
@@ -8,6 +8,11 @@
8
8
  flex-flow: column;
9
9
  }
10
10
 
11
+ .PageHeader--noBorder {
12
+ border-bottom: none;
13
+ padding-bottom: 0;
14
+ }
15
+
11
16
  .PageHeader-contextBar {
12
17
  display: flex;
13
18
  flex-flow: row;
@@ -60,3 +65,8 @@
60
65
  .PageHeader-parentLink {
61
66
  flex: 1 1 auto;
62
67
  }
68
+
69
+ .PageHeader-tabNav {
70
+ margin-top: var(--stack-gap-normal);
71
+ margin-bottom: 0;
72
+ }
@@ -205,6 +205,19 @@ module Primer
205
205
  end
206
206
  }
207
207
 
208
+ # Optional tabs nav at the bottom of the page header
209
+ #
210
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
211
+ renders_one :tab_nav, lambda { |**system_arguments, &block|
212
+ @system_arguments[:classes] = class_names(@system_arguments[:classes], "PageHeader--noBorder")
213
+
214
+ system_arguments = deny_tag_argument(**system_arguments)
215
+ system_arguments[:tag] = :div
216
+ system_arguments[:classes] = class_names(system_arguments[:classes], "PageHeader-tabNav")
217
+
218
+ Primer::Alpha::TabNav.new(**system_arguments, &block)
219
+ }
220
+
208
221
  # @param mobile_menu_label [String] The tooltip label of the mobile menu
209
222
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
210
223
  def initialize(mobile_menu_label: I18n.t("label_more"), **system_arguments)
@@ -237,7 +250,7 @@ module Primer
237
250
 
238
251
  private
239
252
 
240
- def set_action_arguments(system_arguments, scheme: nil, button_action: false)
253
+ def set_action_arguments(system_arguments, scheme: nil)
241
254
  system_arguments[:ml] ||= 2
242
255
  system_arguments[:display] = [:none, :flex]
243
256
  system_arguments[:size] = :medium
@@ -0,0 +1 @@
1
+ .SubHeader{align-items:center;display:grid;flex-wrap:wrap;grid-template-areas:"left middle right" "bottom bottom bottom";grid-template-columns:auto 1fr auto;row-gap:16px}.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;margin-bottom:16px}.SubHeader-leftPane{align-items:center;display:flex;gap:12px;grid-area:left;width:100%}.SubHeader-filterContainer{display:flex;gap:8px;width:100%}
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "open_project/sub_header",
3
+ "selectors": [
4
+ ".SubHeader",
5
+ ".SubHeader-rightPane",
6
+ ".SubHeader-middlePane",
7
+ ".SubHeader-bottomPane",
8
+ ".SubHeader-leftPane",
9
+ ".SubHeader-filterContainer"
10
+ ]
11
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["sub_header.pcss"],"names":[],"mappings":"AAEA,WAKI,kBAAmB,CAJnB,YAAa,CAOb,cAAe,CANf,8DAA+D,CAC/D,mCAAoC,CACpC,YAKJ,CAEA,qBAGI,kBAAmB,CACnB,eAAgB,CAFhB,YAAa,CADb,eAIJ,CAEA,sBACI,gBAAiB,CACjB,iBACJ,CAEA,sBACI,gBAAiB,CACjB,kBACJ,CAEA,oBAGI,kBAAmB,CADnB,YAAa,CAGb,QAAS,CAJT,cAAe,CAGf,UAEJ,CAEA,2BACI,YAAa,CAEb,OAAQ,CADR,UAEJ","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 row-gap: 16px;\n align-items: center;\n\n /* When the filter input is expanded in mobile, we switch to a flex layout */\n flex-wrap: wrap;\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 margin-bottom: 16px;\n}\n\n.SubHeader-leftPane {\n grid-area: left;\n display: flex;\n align-items: center;\n width: 100%;\n gap: 12px;\n}\n\n.SubHeader-filterContainer {\n display: flex;\n width: 100%;\n gap: 8px;\n}\n"]}
@@ -0,0 +1,25 @@
1
+ <%= render Primer::BaseComponent.new(**@system_arguments) do %>
2
+ <div class="SubHeader-leftPane">
3
+ <%= render @filter_container do %>
4
+ <%= filter_input %>
5
+ <%= render @mobile_filter_cancel do
6
+ I18n.t("button_cancel")
7
+ end if @mobile_filter_cancel.present? %>
8
+ <% end if @filter_container.present? %>
9
+ <%= render @mobile_filter_trigger if @mobile_filter_trigger.present? %>
10
+ <%= filter_button %>
11
+ </div>
12
+ <div class="SubHeader-middlePane" data-targets="<%= HIDDEN_FILTER_TARGET_SELECTOR %>">
13
+ <%= text %>
14
+ </div>
15
+ <div class="SubHeader-rightPane" data-targets="<%= HIDDEN_FILTER_TARGET_SELECTOR %>">
16
+ <% actions.each do |action| %>
17
+ <%= action %>
18
+ <% end %>
19
+ </div>
20
+ <% if bottom_pane_component.present? %>
21
+ <div class="SubHeader-bottomPane">
22
+ <%= bottom_pane_component %>
23
+ </div>
24
+ <% end %>
25
+ <% end %>
@@ -0,0 +1,43 @@
1
+ /* CSS for SubHeader */
2
+
3
+ .SubHeader {
4
+ display: grid;
5
+ grid-template-areas: "left middle right" "bottom bottom bottom";
6
+ grid-template-columns: auto 1fr auto;
7
+ row-gap: 16px;
8
+ align-items: center;
9
+
10
+ /* When the filter input is expanded in mobile, we switch to a flex layout */
11
+ flex-wrap: wrap;
12
+ }
13
+
14
+ .SubHeader-rightPane {
15
+ grid-area: right;
16
+ display: flex;
17
+ align-items: center;
18
+ column-gap: 12px;
19
+ }
20
+
21
+ .SubHeader-middlePane {
22
+ grid-area: middle;
23
+ text-align: center;
24
+ }
25
+
26
+ .SubHeader-bottomPane {
27
+ grid-area: bottom;
28
+ margin-bottom: 16px;
29
+ }
30
+
31
+ .SubHeader-leftPane {
32
+ grid-area: left;
33
+ display: flex;
34
+ align-items: center;
35
+ width: 100%;
36
+ gap: 12px;
37
+ }
38
+
39
+ .SubHeader-filterContainer {
40
+ display: flex;
41
+ width: 100%;
42
+ gap: 8px;
43
+ }
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module OpenProject
5
+ # The SubHeader contains specific actions to modify the page content below, e.g a filter button or a create button
6
+ # It should not be used stand alone, but in combination with a PageHeader, either as a direct sibling or as part of a tab content
7
+ class SubHeader < Primer::Component
8
+ status :open_project
9
+
10
+ HIDDEN_FILTER_TARGET_SELECTOR = "sub-header.hiddenItemsOnExpandedFilter"
11
+ SHOWN_FILTER_TARGET_SELECTOR = "sub-header.shownItemsOnExpandedFilter"
12
+
13
+ # A button or custom content that will render on the right-hand side of the component.
14
+ #
15
+ # To render a button, call the `with_button` method, which accepts the arguments accepted by <%= link_to_component(Primer::Beta::Button) %>.
16
+ #
17
+ # To render custom content, call the `with_button_component` method and pass a block that returns HTML.
18
+ renders_many :actions, types: {
19
+ button: {
20
+ renders: lambda { |icon: nil, **kwargs|
21
+ if icon
22
+ Primer::Beta::IconButton.new(icon: icon, **kwargs)
23
+ else
24
+ Primer::Beta::Button.new(**kwargs)
25
+ end
26
+ },
27
+ },
28
+ component: {
29
+ # A generic slot to render whatever component you like on the right side
30
+ renders: lambda { |**kwargs|
31
+ deny_tag_argument(**kwargs)
32
+ kwargs[:tag] = :div
33
+ Primer::BaseComponent.new(**kwargs)
34
+ },
35
+ }
36
+ }
37
+
38
+ renders_one :filter_input, lambda { |name:, label:, **system_arguments|
39
+ system_arguments[:classes] = class_names(
40
+ system_arguments[:classes],
41
+ "SubHeader-filterInput"
42
+ )
43
+ system_arguments[:placeholder] ||= I18n.t("button_filter")
44
+ system_arguments[:leading_visual] ||= { icon: :search }
45
+ system_arguments[:visually_hide_label] ||= true
46
+
47
+ system_arguments[:data] ||= {}
48
+ system_arguments[:data][:target]= "sub-header.filterInput"
49
+
50
+
51
+ @mobile_filter_trigger = Primer::Beta::IconButton.new(icon: system_arguments[:leading_visual][:icon],
52
+ display: [:inline_flex, :none],
53
+ aria: {label: label },
54
+ "data-action": "click:sub-header#expandFilterInput",
55
+ "data-targets": HIDDEN_FILTER_TARGET_SELECTOR)
56
+
57
+ @mobile_filter_cancel = Primer::Beta::Button.new(scheme: :invisible,
58
+ display: :none,
59
+ data: {
60
+ targets: SHOWN_FILTER_TARGET_SELECTOR,
61
+ action: "click:sub-header#collapseFilterInput"})
62
+
63
+
64
+ Primer::Alpha::TextField.new(name: name, label: label, **system_arguments)
65
+ }
66
+
67
+
68
+ # A button or custom content that will render on the left-hand side of the component, next to the filter input.
69
+ #
70
+ # To render a button, call the `with_filter_button` method, which accepts the arguments accepted by <%= link_to_component(Primer::Beta::Button) %>.
71
+ #
72
+ # To render custom content, call the `with_filter_component` method and pass a block that returns HTML.
73
+ renders_one :filter_button, types: {
74
+ button: {
75
+ renders: lambda { |icon: nil, **kwargs|
76
+ kwargs[:classes] = class_names(
77
+ kwargs[:classes],
78
+ "SubHeader-filterButton"
79
+ )
80
+ kwargs[:data] ||= {}
81
+ kwargs[:data][:targets] ||= HIDDEN_FILTER_TARGET_SELECTOR
82
+
83
+ if icon
84
+ Primer::Beta::IconButton.new(icon: icon, **kwargs)
85
+ else
86
+ Primer::Beta::Button.new(**kwargs)
87
+ end
88
+ },
89
+
90
+ as: :filter_button
91
+ },
92
+ component: {
93
+ # A generic slot to render a custom filter component
94
+ renders: lambda { |**kwargs|
95
+ deny_tag_argument(**kwargs)
96
+ kwargs[:tag] = :div
97
+ kwargs[:data] ||= {}
98
+ kwargs[:data][:targets] ||= HIDDEN_FILTER_TARGET_SELECTOR
99
+
100
+ Primer::BaseComponent.new(**kwargs)
101
+ },
102
+
103
+ as: :filter_component
104
+ }
105
+ }
106
+
107
+ renders_one :text, lambda { |**system_arguments|
108
+ system_arguments[:font_weight] ||= :bold
109
+
110
+ Primer::Beta::Text.new(**system_arguments)
111
+
112
+ }
113
+
114
+ # A slot for a generic component which will be shown in a second row below the rest, spanning the whole width
115
+ renders_one :bottom_pane_component, lambda { |**system_arguments|
116
+ deny_tag_argument(**system_arguments)
117
+ system_arguments[:tag] = :div
118
+
119
+ Primer::BaseComponent.new(**system_arguments)
120
+
121
+ }
122
+
123
+
124
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
125
+ def initialize(**system_arguments)
126
+ @system_arguments = system_arguments
127
+ @system_arguments[:tag] = :"sub-header"
128
+
129
+ @filter_container = Primer::BaseComponent.new(tag: :div,
130
+ classes: "SubHeader-filterContainer",
131
+ display: [:none, :flex],
132
+ data: { targets: SHOWN_FILTER_TARGET_SELECTOR })
133
+
134
+ @system_arguments[:classes] = class_names(
135
+ "SubHeader",
136
+ system_arguments[:classes]
137
+ )
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,13 @@
1
+ declare class SubHeaderElement extends HTMLElement {
2
+ filterInput: HTMLElement;
3
+ hiddenItemsOnExpandedFilter: HTMLElement[];
4
+ shownItemsOnExpandedFilter: HTMLElement[];
5
+ expandFilterInput(): void;
6
+ collapseFilterInput(): void;
7
+ }
8
+ declare global {
9
+ interface Window {
10
+ SubHeaderElement: typeof SubHeaderElement;
11
+ }
12
+ }
13
+ export {};
@@ -0,0 +1,44 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { controller, target, targets } from '@github/catalyst';
8
+ let SubHeaderElement = class SubHeaderElement extends HTMLElement {
9
+ expandFilterInput() {
10
+ for (const item of this.hiddenItemsOnExpandedFilter) {
11
+ item.classList.add('d-none');
12
+ }
13
+ for (const item of this.shownItemsOnExpandedFilter) {
14
+ item.classList.remove('d-none');
15
+ }
16
+ this.classList.add('d-flex');
17
+ this.filterInput.focus();
18
+ }
19
+ collapseFilterInput() {
20
+ for (const item of this.hiddenItemsOnExpandedFilter) {
21
+ item.classList.remove('d-none');
22
+ }
23
+ for (const item of this.shownItemsOnExpandedFilter) {
24
+ item.classList.add('d-none');
25
+ }
26
+ this.classList.remove('d-flex');
27
+ }
28
+ };
29
+ __decorate([
30
+ target
31
+ ], SubHeaderElement.prototype, "filterInput", void 0);
32
+ __decorate([
33
+ targets
34
+ ], SubHeaderElement.prototype, "hiddenItemsOnExpandedFilter", void 0);
35
+ __decorate([
36
+ targets
37
+ ], SubHeaderElement.prototype, "shownItemsOnExpandedFilter", void 0);
38
+ SubHeaderElement = __decorate([
39
+ controller
40
+ ], SubHeaderElement);
41
+ if (!window.customElements.get('sub-header')) {
42
+ window.SubHeaderElement = SubHeaderElement;
43
+ window.customElements.define('sub-header', SubHeaderElement);
44
+ }
@@ -0,0 +1,45 @@
1
+ import {controller, target, targets} from '@github/catalyst'
2
+
3
+ @controller
4
+ class SubHeaderElement extends HTMLElement {
5
+ @target filterInput: HTMLElement
6
+ @targets hiddenItemsOnExpandedFilter: HTMLElement[]
7
+ @targets shownItemsOnExpandedFilter: HTMLElement[]
8
+
9
+ expandFilterInput() {
10
+ for (const item of this.hiddenItemsOnExpandedFilter) {
11
+ item.classList.add('d-none')
12
+ }
13
+
14
+ for (const item of this.shownItemsOnExpandedFilter) {
15
+ item.classList.remove('d-none')
16
+ }
17
+
18
+ this.classList.add('d-flex')
19
+
20
+ this.filterInput.focus()
21
+ }
22
+
23
+ collapseFilterInput() {
24
+ for (const item of this.hiddenItemsOnExpandedFilter) {
25
+ item.classList.remove('d-none')
26
+ }
27
+
28
+ for (const item of this.shownItemsOnExpandedFilter) {
29
+ item.classList.add('d-none')
30
+ }
31
+
32
+ this.classList.remove('d-flex')
33
+ }
34
+ }
35
+
36
+ declare global {
37
+ interface Window {
38
+ SubHeaderElement: typeof SubHeaderElement
39
+ }
40
+ }
41
+
42
+ if (!window.customElements.get('sub-header')) {
43
+ window.SubHeaderElement = SubHeaderElement
44
+ window.customElements.define('sub-header', SubHeaderElement)
45
+ }
@@ -24,3 +24,4 @@ import '../../../lib/primer/forms/toggle_switch_input';
24
24
  import './alpha/action_menu/action_menu_element';
25
25
  import './open_project/page_header_element';
26
26
  import './open_project/zen_mode_button';
27
+ import './open_project/sub_header_element';
@@ -24,3 +24,4 @@ import '../../../lib/primer/forms/toggle_switch_input';
24
24
  import './alpha/action_menu/action_menu_element';
25
25
  import './open_project/page_header_element';
26
26
  import './open_project/zen_mode_button';
27
+ import './open_project/sub_header_element';
@@ -44,3 +44,4 @@
44
44
  @import "./open_project/drag_handle.pcss";
45
45
  @import "./open_project/border_grid.pcss";
46
46
  @import "./open_project/input_group.pcss";
47
+ @import "./open_project/sub_header.pcss";
@@ -24,3 +24,4 @@ import '../../../lib/primer/forms/toggle_switch_input'
24
24
  import './alpha/action_menu/action_menu_element'
25
25
  import './open_project/page_header_element'
26
26
  import './open_project/zen_mode_button'
27
+ import './open_project/sub_header_element'
@@ -5,8 +5,8 @@ module Primer
5
5
  module ViewComponents
6
6
  module VERSION
7
7
  MAJOR = 0
8
- MINOR = 30
9
- PATCH = 1
8
+ MINOR = 32
9
+ PATCH = 0
10
10
 
11
11
  STRING = [MAJOR, MINOR, PATCH].join(".")
12
12
  end
@@ -14,4 +14,11 @@
14
14
  <% end %>
15
15
  <% end %>
16
16
  <% end %>
17
+ <% if with_tab_nav %>
18
+ <% header.with_tab_nav(label: "label") do |nav|%>
19
+ <% nav.with_tab(selected: true, href: "#") { "Tab 1" } %>
20
+ <% nav.with_tab(href: "#") { "Tab 2" } %>
21
+ <% nav.with_tab(href: "#") { "Tab 3" } %>
22
+ <% end %>
23
+ <% end %>
17
24
  <% end %>
@@ -23,12 +23,14 @@ module Primer
23
23
  # @param description [String] text
24
24
  # @param with_leading_action [Symbol] octicon
25
25
  # @param with_actions [Boolean]
26
+ # @param with_tab_nav [Boolean]
26
27
  def playground(
27
28
  variant: :medium,
28
29
  title: "Hello",
29
30
  description: "Last updated 5 minutes ago by XYZ.",
30
31
  with_leading_action: :"none",
31
- with_actions: true
32
+ with_actions: true,
33
+ with_tab_nav: false
32
34
  )
33
35
  breadcrumb_items = [{ href: "/foo", text: "Foo" }, { href: "/bar", text: "Bar" }, "Baz"]
34
36
 
@@ -37,7 +39,8 @@ module Primer
37
39
  description: description,
38
40
  with_leading_action: with_leading_action,
39
41
  with_actions: with_actions,
40
- breadcrumb_items: breadcrumb_items })
42
+ breadcrumb_items: breadcrumb_items,
43
+ with_tab_nav: with_tab_nav})
41
44
  end
42
45
 
43
46
  # @label Large title
@@ -167,6 +170,23 @@ module Primer
167
170
  header.with_breadcrumbs(breadcrumb_items, selected_item_font_weight: :normal)
168
171
  end
169
172
  end
173
+
174
+ # @label With tab nav
175
+ #
176
+ def tab_nav
177
+ render(Primer::OpenProject::PageHeader.new) do |header|
178
+ header.with_title { "Hello" }
179
+ header.with_breadcrumbs([{ href: "/foo", text: "Foo" }, { href: "/bar", text: "Bar" }, "Baz"])
180
+ header.with_description { "Last updated 5 minutes ago by XYZ." }
181
+ header.with_tab_nav(label: "label") do |nav|
182
+ Array.new(3) do |i|
183
+ nav.with_tab(selected: i.zero? , href: "#") do |tab|
184
+ tab.with_text { "Tab #{i + 1}" }
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
170
190
  end
171
191
  end
172
192
  end
@@ -0,0 +1,15 @@
1
+ <%= render(Primer::OpenProject::SubHeader.new) do |component| %>
2
+ <% component.with_filter_input(name: "filter", label: "Filter") %>
3
+
4
+ <% component.with_action_component do %>
5
+ <%= render Primer::Alpha::ActionMenu.new(menu_id: "menu-1") do |menu|
6
+ menu.with_show_button(icon: :"op-kebab-vertical", "aria-label": "Menu")
7
+ menu.with_item(label: "Subitem 1") do |item|
8
+ item.with_leading_visual_icon(icon: :paste)
9
+ end
10
+ menu.with_item(label: "Subitem 2") do |item|
11
+ item.with_leading_visual_icon(icon: :log)
12
+ end
13
+ end %>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <%= render(Primer::OpenProject::SubHeader.new) do |component| %>
2
+ <% component.with_filter_input(name: "filter", label: "Filter") %>
3
+ <% component.with_filter_component do %>
4
+ <!-- Render any custom filter component that you want -->
5
+ <%= render(Primer::Beta::IconButton.new(icon: "filter", "aria-label": "Filter")) %>
6
+ <% end %>
7
+ <% component.with_bottom_pane_component do %>
8
+ <!-- Render any custom component that you want -->
9
+ <!-- The component will be shown in a second row below the rest, spanning the whole width -->
10
+ <%= render(Primer::BaseComponent.new(tag: :div, bg: :accent, p: 3)) { 'Render any custom content here (e.g a filter area)' } %>
11
+ <% end %>
12
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <%= render(Primer::OpenProject::SubHeader.new) do |component| %>
2
+ <% component.with_filter_input(name: "filter", label: "Filter") %>
3
+
4
+ <% component.with_action_component do %>
5
+ <%= render(Primer::Beta::ButtonGroup.new) do |group|
6
+ group.with_button { "Button 1" }
7
+ group.with_button { "Button 2" }
8
+ group.with_button { "Button 3" }
9
+ end %>
10
+ <% end %>
11
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%= render(Primer::OpenProject::SubHeader.new) do |component| %>
2
+ <% component.with_filter_input(name: "filter", label: "Filter") %>
3
+ <% component.with_filter_component do %>
4
+ <!-- Render any custom filter component that you want -->
5
+ <!-- Note, that you can pass an ID for the bottom pane if you need to access that area for further filter actions-->
6
+ <%= render(Primer::Beta::IconButton.new(icon: "filter", "aria-label": "Filter")) %>
7
+ <% end %>
8
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <%= render(Primer::OpenProject::SubHeader.new) do |component| %>
2
+ <% component.with_filter_input(name: "filter", label: "Filter") %>
3
+
4
+ <% component.with_action_component do %>
5
+ <%= render(Primer::Alpha::Dialog.new(id: "dialog-one", title: "Dialog")) do |d| %>
6
+ <% d.with_show_button { "Show Dialog" } %>
7
+ <% d.with_body do %>
8
+ Hello world!
9
+ <% end %>
10
+ <% end %>
11
+ <% end %>
12
+ <% end %>