openproject-primer_view_components 0.30.1 → 0.32.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 (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 %>