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.
- 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 %>
|