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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/app/assets/javascripts/app/components/primer/open_project/sub_header_element.d.ts +13 -0
- data/app/assets/javascripts/app/components/primer/primer.d.ts +1 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/assets/styles/primer_view_components.css +1 -1
- data/app/assets/styles/primer_view_components.css.map +1 -1
- data/app/components/primer/open_project/page_header.css +1 -1
- data/app/components/primer/open_project/page_header.css.json +3 -1
- data/app/components/primer/open_project/page_header.css.map +1 -1
- data/app/components/primer/open_project/page_header.html.erb +1 -0
- data/app/components/primer/open_project/page_header.pcss +10 -0
- data/app/components/primer/open_project/page_header.rb +14 -1
- data/app/components/primer/open_project/sub_header.css +1 -0
- data/app/components/primer/open_project/sub_header.css.json +11 -0
- data/app/components/primer/open_project/sub_header.css.map +1 -0
- data/app/components/primer/open_project/sub_header.html.erb +25 -0
- data/app/components/primer/open_project/sub_header.pcss +43 -0
- data/app/components/primer/open_project/sub_header.rb +141 -0
- data/app/components/primer/open_project/sub_header_element.d.ts +13 -0
- data/app/components/primer/open_project/sub_header_element.js +44 -0
- data/app/components/primer/open_project/sub_header_element.ts +45 -0
- data/app/components/primer/primer.d.ts +1 -0
- data/app/components/primer/primer.js +1 -0
- data/app/components/primer/primer.pcss +1 -0
- data/app/components/primer/primer.ts +1 -0
- data/lib/primer/view_components/version.rb +2 -2
- data/previews/primer/open_project/page_header_preview/playground.html.erb +7 -0
- data/previews/primer/open_project/page_header_preview.rb +22 -2
- data/previews/primer/open_project/sub_header_preview/action_menu_buttons.html.erb +15 -0
- data/previews/primer/open_project/sub_header_preview/bottom_pane.html.erb +12 -0
- data/previews/primer/open_project/sub_header_preview/button_group.html.erb +11 -0
- data/previews/primer/open_project/sub_header_preview/custom_filter_button.html.erb +8 -0
- data/previews/primer/open_project/sub_header_preview/dialog_buttons.html.erb +12 -0
- data/previews/primer/open_project/sub_header_preview.rb +88 -0
- data/static/arguments.json +16 -0
- data/static/audited_at.json +1 -0
- data/static/classes.json +24 -0
- data/static/constants.json +4 -0
- data/static/info_arch.json +196 -0
- data/static/previews.json +125 -0
- data/static/statuses.json +1 -0
- 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"]}
|
@@ -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
|
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 @@
|
|
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
|
+
}
|
@@ -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 %>
|