openproject-primer_view_components 0.65.0 → 0.66.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 +6 -0
- 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.rb +1 -1
- data/app/components/primer/open_project/sub_header/button.rb +43 -0
- data/app/components/primer/open_project/sub_header/button_group.rb +16 -0
- data/app/components/primer/open_project/sub_header/menu.rb +67 -0
- data/app/components/primer/open_project/sub_header/segmented_control.rb +16 -0
- data/app/components/primer/open_project/sub_header.css +1 -1
- data/app/components/primer/open_project/sub_header.css.json +4 -1
- data/app/components/primer/open_project/sub_header.css.map +1 -1
- data/app/components/primer/open_project/sub_header.html.erb +21 -0
- data/app/components/primer/open_project/sub_header.pcss +29 -3
- data/app/components/primer/open_project/sub_header.rb +105 -21
- data/lib/primer/view_components/version.rb +1 -1
- data/previews/primer/open_project/page_header_preview/create_action.html.erb +1 -2
- data/previews/primer/open_project/sub_header_preview/action_menu_buttons.html.erb +7 -10
- data/previews/primer/open_project/sub_header_preview/button_group.html.erb +13 -7
- data/previews/primer/open_project/sub_header_preview/custom_filter_button.html.erb +1 -1
- data/previews/primer/open_project/sub_header_preview/dialog_buttons.html.erb +9 -8
- data/previews/primer/open_project/sub_header_preview.rb +26 -7
- data/static/arguments.json +124 -0
- data/static/audited_at.json +4 -0
- data/static/classes.json +3 -0
- data/static/constants.json +25 -1
- data/static/info_arch.json +245 -0
- data/static/previews.json +13 -0
- data/static/statuses.json +4 -0
- metadata +6 -2
@@ -150,7 +150,7 @@ module Primer
|
|
150
150
|
@mobile_segmented_control = Primer::Alpha::SegmentedControl.new(**system_arguments,
|
151
151
|
**mobile_args,
|
152
152
|
mr: 2,
|
153
|
-
display:
|
153
|
+
display: MOBILE_ACTIONS_DISPLAY)
|
154
154
|
@mobile_segmented_control_block = block
|
155
155
|
|
156
156
|
Primer::Alpha::SegmentedControl.new(**system_arguments)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
# A Helper class to create a Button with required icon inside the SubHeader action slot
|
6
|
+
# Do not use standalone
|
7
|
+
class SubHeader::Button < Primer::Component
|
8
|
+
status :open_project
|
9
|
+
|
10
|
+
renders_one :leading_visual_icon, lambda { |**system_arguments|
|
11
|
+
# Do nothing as this slot is reserved for the enforced leading icon
|
12
|
+
}
|
13
|
+
|
14
|
+
# @param icon [Symbol] The name of an <%= link_to_octicons %> icon to use as leading visual
|
15
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
16
|
+
def initialize(icon:, **system_arguments)
|
17
|
+
@icon = icon
|
18
|
+
@button = Primer::Beta::Button.new(**system_arguments)
|
19
|
+
end
|
20
|
+
|
21
|
+
delegate :trailing_visual_icon?, :trailing_visual_icon, :with_trailing_visual_icon, :with_trailing_visual_content_icon,
|
22
|
+
:trailing_visual_counter?, :trailing_visual_counter, :with_trailing_visual_counter, :with_trailing_visual_content_counter,
|
23
|
+
:trailing_visual_label?, :trailing_visual_label, :with_trailing_visual_label, :with_trailing_visual_content_label,
|
24
|
+
:trailing_action?, :trailing_action, :with_trailing_action, :with_trailing_action_content,
|
25
|
+
:tooltip?, :tooltip, :with_tooltip, :with_tooltip,
|
26
|
+
to: :@button
|
27
|
+
|
28
|
+
def before_render
|
29
|
+
if leading_visual_icon.present?
|
30
|
+
raise ArgumentError,
|
31
|
+
"Do not use the leading_visual_icon slot within the SubHeader, as it is reserved. Instead provide a leading_icon within the subHeader button slot"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def call
|
36
|
+
render(@button) do |button|
|
37
|
+
button.with_leading_visual_icon(icon: @icon)
|
38
|
+
content
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
# A Helper class to create ButtonGroups inside the SubHeader action slot
|
6
|
+
# Do not use standalone
|
7
|
+
class SubHeader::ButtonGroup < Primer::Beta::ButtonGroup
|
8
|
+
status :open_project
|
9
|
+
|
10
|
+
def with_button(icon:, **system_arguments, &block)
|
11
|
+
system_arguments[:icon] = icon
|
12
|
+
super(component_klass: Primer::OpenProject::SubHeader::ButtonGroup, **system_arguments, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
# A Helper class to create an ActionMenu with a required icon on the trigger button.
|
6
|
+
# It is meant to be used inside the SubHeader
|
7
|
+
# Do not use standalone
|
8
|
+
class SubHeader::Menu < Primer::Component
|
9
|
+
status :open_project
|
10
|
+
|
11
|
+
renders_one :show_button, lambda { |**system_arguments|
|
12
|
+
# Do nothing as this slot is reserved for the enforced leading icon
|
13
|
+
}
|
14
|
+
|
15
|
+
def set_show_button(**system_arguments)
|
16
|
+
aria_label = aria("label", system_arguments) || @label
|
17
|
+
|
18
|
+
if @icon_only
|
19
|
+
@menu.with_show_button(icon: @leading_icon, "aria-label": aria_label, **system_arguments)
|
20
|
+
else
|
21
|
+
@menu.with_show_button("aria-label": aria_label, **system_arguments) do |button|
|
22
|
+
button.with_leading_visual_icon(icon: @leading_icon)
|
23
|
+
button.with_trailing_action_icon(icon: @trailing_icon) unless @trailing_icon.nil?
|
24
|
+
@label
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param icon_only [Boolean] Whether the trigger button is an IconButton
|
30
|
+
# @param leading_icon [Symbol] Name of Octicon icon to use as either leading icon or IconButton.
|
31
|
+
# @param label [String] The button label
|
32
|
+
# @param button_arguments [Hash] Additional arguments for the button
|
33
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
34
|
+
def initialize(icon_only: false, leading_icon:, label:, trailing_icon: nil, button_arguments: {}, **system_arguments)
|
35
|
+
@icon_only = icon_only
|
36
|
+
@leading_icon = leading_icon
|
37
|
+
@trailing_icon = trailing_icon
|
38
|
+
@label = label
|
39
|
+
|
40
|
+
if @label.nil? || @label.empty?
|
41
|
+
raise ArgumentError, "You need to provide a valid label."
|
42
|
+
end
|
43
|
+
|
44
|
+
@button_arguments = button_arguments
|
45
|
+
|
46
|
+
@menu = Primer::Alpha::ActionMenu.new(**system_arguments)
|
47
|
+
end
|
48
|
+
|
49
|
+
delegate :with_item, :with_divider, :with_avatar_item, :with_group,
|
50
|
+
to: :@menu
|
51
|
+
|
52
|
+
def before_render
|
53
|
+
if show_button
|
54
|
+
raise ArgumentError,
|
55
|
+
"Do not use the show_button slot within the SubHeader, as it is reserved. Instead provide a leading_icon within the subHeader button slot"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def call
|
60
|
+
render(@menu) do
|
61
|
+
set_show_button(**@button_arguments)
|
62
|
+
content
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
# A Helper class to create SegmentedControls inside the SubHeader action slot
|
6
|
+
# Do not use standalone
|
7
|
+
class SubHeader::SegmentedControl < Primer::Alpha::SegmentedControl
|
8
|
+
status :open_project
|
9
|
+
|
10
|
+
def with_item(icon:, **system_arguments, &block)
|
11
|
+
system_arguments[:icon] = icon
|
12
|
+
super(component_klass: Primer::OpenProject::SubHeader::SegmentedControl, **system_arguments, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1 +1 @@
|
|
1
|
-
.SubHeader{align-items:center;display:grid;grid-template-areas:"left middle right" "bottom bottom bottom";grid-template-columns:auto 1fr auto;margin-bottom:
|
1
|
+
.SubHeader{align-items:center;display:grid;grid-template-areas:"left middle right" "bottom bottom bottom";grid-template-columns:auto 1fr auto;margin-bottom:var(--base-size-16)}.SubHeader--expandedSearch{grid-template-areas:"left left left" "bottom bottom bottom"}.SubHeader-rightPane{align-items:center;column-gap:12px;display:flex;grid-area:right}.SubHeader-middlePane{grid-area:middle;text-align:center}.SubHeader-bottomPane{grid-area:bottom}.SubHeader-leftPane{align-items:center;display:flex;grid-area:left;width:100%}:is(.SubHeader-leftPane [class*=FormControl-input-width--]):not(.FormControl-input-width--auto){width:100vw}.SubHeader-filterContainer{display:flex;flex-basis:max-content;gap:8px;width:100%}.SubHeader-filterInput_hiddenClearButton+.FormControl-input-trailingAction{display:none}@media (max-width:543.98px){.SubHeader{grid-template-areas:"left right" "middle middle" "bottom bottom";grid-template-columns:1fr auto}.SubHeader--emptyLeftPane{grid-template-areas:"middle middle right" "bottom bottom bottom";grid-template-columns:auto 1fr auto}.SubHeader--emptyLeftPane .SubHeader-middlePane{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.SubHeader-middlePane{text-align:left}.SubHeader-middlePane:has(>*){margin-top:var(--stack-gap-normal)}}
|
@@ -9,6 +9,9 @@
|
|
9
9
|
".SubHeader-leftPane",
|
10
10
|
":is(.SubHeader-leftPane [class*=FormControl-input-width--]):not(.FormControl-input-width--auto)",
|
11
11
|
".SubHeader-filterContainer",
|
12
|
-
".SubHeader-filterInput_hiddenClearButton
|
12
|
+
".SubHeader-filterInput_hiddenClearButton+.FormControl-input-trailingAction",
|
13
|
+
".SubHeader--emptyLeftPane",
|
14
|
+
".SubHeader--emptyLeftPane .SubHeader-middlePane",
|
15
|
+
".SubHeader-middlePane:has(>*)"
|
13
16
|
]
|
14
17
|
}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["sub_header.pcss"],"names":[],"mappings":"AAEA,WAII,kBAAmB,CAHnB,YAAa,CACb,8DAA+D,CAC/D,mCAAoC,CAEpC,
|
1
|
+
{"version":3,"sources":["sub_header.pcss"],"names":[],"mappings":"AAEA,WAII,kBAAmB,CAHnB,YAAa,CACb,8DAA+D,CAC/D,mCAAoC,CAEpC,iCACJ,CAEA,2BACI,2DACJ,CAEA,qBAGI,kBAAmB,CACnB,eAAgB,CAFhB,YAAa,CADb,eAIJ,CAEA,sBACI,gBAAiB,CACjB,iBACJ,CAEA,sBACI,gBACJ,CAEA,oBAGI,kBAAmB,CADnB,YAAa,CADb,cAAe,CAGf,UASJ,CAJQ,gGACI,WACJ,CAIR,2BACI,YAAa,CACb,sBAAuB,CAEvB,OAAQ,CADR,UAEJ,CAEA,2EACE,YACF,CAEA,4BACI,WACI,gEAAiE,CACjE,8BACJ,CAEA,0BACI,gEAAiE,CACjE,mCACJ,CAEA,gDAGI,eAAgB,CADhB,sBAAuB,CADvB,kBAGJ,CAEA,sBACI,eACJ,CAEA,8BACI,kCACJ,CACJ","file":"sub_header.css","sourcesContent":["/* CSS for SubHeader */\n\n.SubHeader {\n display: grid;\n grid-template-areas: \"left middle right\" \"bottom bottom bottom\";\n grid-template-columns: auto 1fr auto;\n align-items: center;\n margin-bottom: var(--base-size-16);\n}\n\n.SubHeader--expandedSearch {\n grid-template-areas: \"left left left\" \"bottom bottom bottom\";\n}\n\n.SubHeader-rightPane {\n grid-area: right;\n display: flex;\n align-items: center;\n column-gap: 12px;\n}\n\n.SubHeader-middlePane {\n grid-area: middle;\n text-align: center;\n}\n\n.SubHeader-bottomPane {\n grid-area: bottom;\n}\n\n.SubHeader-leftPane {\n grid-area: left;\n display: flex;\n align-items: center;\n width: 100%;\n\n /* Since the container is not full width (due to the grid around it)\n we want it to grow, and then be limited by the max-width of the \"FormControl-input-width--xy\" class */\n & [class*='FormControl-input-width--'] {\n &:not(.FormControl-input-width--auto) {\n width: 100vw;\n }\n }\n}\n\n.SubHeader-filterContainer {\n display: flex;\n flex-basis: max-content;\n width: 100%;\n gap: 8px;\n}\n\n.SubHeader-filterInput_hiddenClearButton + .FormControl-input-trailingAction {\n display: none;\n}\n\n@media (max-width: 543.98px) {\n .SubHeader {\n grid-template-areas: \"left right\" \"middle middle\" \"bottom bottom\";\n grid-template-columns: 1fr auto;\n }\n\n .SubHeader--emptyLeftPane {\n grid-template-areas: \"middle middle right\" \"bottom bottom bottom\";\n grid-template-columns: auto 1fr auto;\n }\n\n .SubHeader--emptyLeftPane .SubHeader-middlePane {\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n }\n\n .SubHeader-middlePane {\n text-align: left;\n }\n\n .SubHeader-middlePane:has(> *) {\n margin-top: var(--stack-gap-normal);\n }\n}\n"]}
|
@@ -6,17 +6,38 @@
|
|
6
6
|
I18n.t("button_cancel")
|
7
7
|
end if @mobile_filter_cancel.present? %>
|
8
8
|
<% end if filter_input.present? %>
|
9
|
+
|
9
10
|
<%= render @mobile_filter_trigger if @mobile_filter_trigger.present? %>
|
11
|
+
|
12
|
+
<%= render(@mobile_filter_button) if @mobile_filter_button.present? %>
|
13
|
+
|
10
14
|
<%= filter_button %>
|
15
|
+
|
16
|
+
<%= segmented_control %>
|
17
|
+
|
18
|
+
<% if @segmented_control_block.present? %>
|
19
|
+
<%= render(@mobile_segmented_control) do |control| %>
|
20
|
+
<% @segmented_control_block.call(control) %>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
11
23
|
</div>
|
24
|
+
|
12
25
|
<div class="SubHeader-middlePane" data-targets="<%= HIDDEN_FILTER_TARGET_SELECTOR %>">
|
13
26
|
<%= text %>
|
14
27
|
</div>
|
28
|
+
|
15
29
|
<div class="SubHeader-rightPane" data-targets="<%= HIDDEN_FILTER_TARGET_SELECTOR %>">
|
16
30
|
<% actions.each do |action| %>
|
17
31
|
<%= action %>
|
18
32
|
<% end %>
|
33
|
+
|
34
|
+
<% @mobile_actions.each do |mobile_action| %>
|
35
|
+
<%= render(mobile_action[:component]) do |action| %>
|
36
|
+
<% mobile_action[:block].call(action) %>
|
37
|
+
<% end %>
|
38
|
+
<% end unless @mobile_actions.nil? %>
|
19
39
|
</div>
|
40
|
+
|
20
41
|
<% if bottom_pane_component.present? %>
|
21
42
|
<div class="SubHeader-bottomPane">
|
22
43
|
<%= bottom_pane_component %>
|
@@ -5,7 +5,7 @@
|
|
5
5
|
grid-template-areas: "left middle right" "bottom bottom bottom";
|
6
6
|
grid-template-columns: auto 1fr auto;
|
7
7
|
align-items: center;
|
8
|
-
margin-bottom:
|
8
|
+
margin-bottom: var(--base-size-16);
|
9
9
|
}
|
10
10
|
|
11
11
|
.SubHeader--expandedSearch {
|
@@ -33,7 +33,6 @@
|
|
33
33
|
display: flex;
|
34
34
|
align-items: center;
|
35
35
|
width: 100%;
|
36
|
-
gap: 12px;
|
37
36
|
|
38
37
|
/* Since the container is not full width (due to the grid around it)
|
39
38
|
we want it to grow, and then be limited by the max-width of the "FormControl-input-width--xy" class */
|
@@ -46,10 +45,37 @@
|
|
46
45
|
|
47
46
|
.SubHeader-filterContainer {
|
48
47
|
display: flex;
|
48
|
+
flex-basis: max-content;
|
49
49
|
width: 100%;
|
50
50
|
gap: 8px;
|
51
51
|
}
|
52
52
|
|
53
|
-
.SubHeader-filterInput_hiddenClearButton +
|
53
|
+
.SubHeader-filterInput_hiddenClearButton + .FormControl-input-trailingAction {
|
54
54
|
display: none;
|
55
55
|
}
|
56
|
+
|
57
|
+
@media (max-width: 543.98px) {
|
58
|
+
.SubHeader {
|
59
|
+
grid-template-areas: "left right" "middle middle" "bottom bottom";
|
60
|
+
grid-template-columns: 1fr auto;
|
61
|
+
}
|
62
|
+
|
63
|
+
.SubHeader--emptyLeftPane {
|
64
|
+
grid-template-areas: "middle middle right" "bottom bottom bottom";
|
65
|
+
grid-template-columns: auto 1fr auto;
|
66
|
+
}
|
67
|
+
|
68
|
+
.SubHeader--emptyLeftPane .SubHeader-middlePane {
|
69
|
+
white-space: nowrap;
|
70
|
+
text-overflow: ellipsis;
|
71
|
+
overflow: hidden;
|
72
|
+
}
|
73
|
+
|
74
|
+
.SubHeader-middlePane {
|
75
|
+
text-align: left;
|
76
|
+
}
|
77
|
+
|
78
|
+
.SubHeader-middlePane:has(> *) {
|
79
|
+
margin-top: var(--stack-gap-normal);
|
80
|
+
}
|
81
|
+
}
|
@@ -10,6 +10,9 @@ module Primer
|
|
10
10
|
HIDDEN_FILTER_TARGET_SELECTOR = "sub-header.hiddenItemsOnExpandedFilter"
|
11
11
|
SHOWN_FILTER_TARGET_SELECTOR = "sub-header.shownItemsOnExpandedFilter"
|
12
12
|
|
13
|
+
MOBILE_ACTIONS_DISPLAY = [:flex, :none].freeze
|
14
|
+
DESKTOP_ACTIONS_DISPLAY = [:none, :flex].freeze
|
15
|
+
|
13
16
|
# A button or custom content that will render on the right-hand side of the component.
|
14
17
|
#
|
15
18
|
# To render a button, call the `with_button` method, which accepts the arguments accepted by <%= link_to_component(Primer::Beta::Button) %>.
|
@@ -17,25 +20,63 @@ module Primer
|
|
17
20
|
# To render custom content, call the `with_button_component` method and pass a block that returns HTML.
|
18
21
|
renders_many :actions, types: {
|
19
22
|
button: {
|
20
|
-
renders: lambda { |
|
21
|
-
if
|
22
|
-
|
23
|
+
renders: lambda { |icon_only: false, leading_icon:, label:, **kwargs, &block|
|
24
|
+
if label.nil? || label.empty?
|
25
|
+
raise ArgumentError, "You need to provide a valid label."
|
26
|
+
end
|
27
|
+
|
28
|
+
kwargs[:icon] = leading_icon
|
29
|
+
|
30
|
+
kwargs[:aria] ||= merge_aria(
|
31
|
+
kwargs,
|
32
|
+
{ aria: { label: label } }
|
33
|
+
)
|
34
|
+
|
35
|
+
icon_args = kwargs.deep_dup
|
36
|
+
|
37
|
+
if icon_only
|
38
|
+
Primer::Beta::IconButton.new(**icon_args)
|
23
39
|
else
|
24
|
-
|
40
|
+
@mobile_actions ||= []
|
41
|
+
mobile_component = Primer::Beta::IconButton.new(display: MOBILE_ACTIONS_DISPLAY,
|
42
|
+
**icon_args)
|
43
|
+
@mobile_actions.push({ component: mobile_component, block: block})
|
44
|
+
|
45
|
+
Primer::OpenProject::SubHeader::Button.new(display: DESKTOP_ACTIONS_DISPLAY, **kwargs)
|
25
46
|
end
|
26
47
|
},
|
27
48
|
},
|
28
|
-
|
29
|
-
# A generic slot to render whatever component you like on the right side
|
49
|
+
button_group: {
|
30
50
|
renders: lambda { |**kwargs|
|
31
|
-
|
32
|
-
|
33
|
-
|
51
|
+
Primer::OpenProject::SubHeader::ButtonGroup.new(**kwargs)
|
52
|
+
},
|
53
|
+
},
|
54
|
+
menu: {
|
55
|
+
renders: lambda { |icon_only: false, leading_icon:, label:, button_arguments: {}, **kwargs, &block|
|
56
|
+
if label.nil? || label.empty?
|
57
|
+
raise ArgumentError, "You need to provide a valid label."
|
58
|
+
end
|
59
|
+
|
60
|
+
kwargs[:leading_icon] = leading_icon
|
61
|
+
kwargs[:label] = label
|
62
|
+
kwargs[:button_arguments] = button_arguments
|
63
|
+
|
64
|
+
@mobile_actions ||= []
|
65
|
+
mobile_component = Primer::OpenProject::SubHeader::Menu.new(icon_only: true,
|
66
|
+
display: MOBILE_ACTIONS_DISPLAY,
|
67
|
+
**kwargs)
|
68
|
+
@mobile_actions.push({ component: mobile_component, block: block})
|
69
|
+
|
70
|
+
Primer::OpenProject::SubHeader::Menu.new(icon_only: icon_only,display: DESKTOP_ACTIONS_DISPLAY, **kwargs)
|
34
71
|
},
|
35
72
|
}
|
36
73
|
}
|
37
74
|
|
38
75
|
renders_one :filter_input, lambda { |name:, label:, **system_arguments|
|
76
|
+
if label.nil? || label.empty?
|
77
|
+
raise ArgumentError, "You need to provide a valid label."
|
78
|
+
end
|
79
|
+
|
39
80
|
system_arguments[:classes] = class_names(
|
40
81
|
system_arguments[:classes],
|
41
82
|
"SubHeader-filterInput",
|
@@ -62,6 +103,7 @@ module Primer
|
|
62
103
|
@mobile_filter_trigger = Primer::Beta::IconButton.new(icon: system_arguments[:leading_visual][:icon],
|
63
104
|
display: [:inline_flex, :none],
|
64
105
|
aria: { label: label },
|
106
|
+
mr: 2,
|
65
107
|
"data-action": "click:sub-header#expandFilterInput",
|
66
108
|
"data-targets": HIDDEN_FILTER_TARGET_SELECTOR)
|
67
109
|
|
@@ -83,18 +125,25 @@ module Primer
|
|
83
125
|
# To render custom content, call the `with_filter_component` method and pass a block that returns HTML.
|
84
126
|
renders_one :filter_button, types: {
|
85
127
|
button: {
|
86
|
-
renders: lambda { |
|
87
|
-
kwargs[:
|
88
|
-
|
89
|
-
|
128
|
+
renders: lambda { |icon_only: false, leading_icon: :filter, mobile_label: I18n.t("button_filter"), **kwargs|
|
129
|
+
kwargs[:mr] ||= 2
|
130
|
+
kwargs[:icon] = leading_icon
|
131
|
+
|
132
|
+
kwargs[:aria] ||= merge_aria(
|
133
|
+
kwargs,
|
134
|
+
{ aria: { label: mobile_label } }
|
90
135
|
)
|
91
|
-
kwargs[:data] ||= {}
|
92
|
-
kwargs[:data][:targets] ||= HIDDEN_FILTER_TARGET_SELECTOR
|
93
136
|
|
94
|
-
|
95
|
-
|
137
|
+
icon_args = kwargs.deep_dup
|
138
|
+
icon_args = set_as_hidden_filter_target(icon_args)
|
139
|
+
|
140
|
+
if icon_only
|
141
|
+
Primer::Beta::IconButton.new(**icon_args)
|
96
142
|
else
|
97
|
-
Primer::Beta::
|
143
|
+
@mobile_filter_button = Primer::Beta::IconButton.new(display: MOBILE_ACTIONS_DISPLAY,
|
144
|
+
**icon_args)
|
145
|
+
|
146
|
+
Primer::OpenProject::SubHeader::Button.new(display: DESKTOP_ACTIONS_DISPLAY, **kwargs)
|
98
147
|
end
|
99
148
|
},
|
100
149
|
|
@@ -105,8 +154,7 @@ module Primer
|
|
105
154
|
renders: lambda { |**kwargs|
|
106
155
|
deny_tag_argument(**kwargs)
|
107
156
|
kwargs[:tag] = :div
|
108
|
-
kwargs
|
109
|
-
kwargs[:data][:targets] ||= HIDDEN_FILTER_TARGET_SELECTOR
|
157
|
+
kwargs = set_as_hidden_filter_target(kwargs)
|
110
158
|
|
111
159
|
Primer::BaseComponent.new(**kwargs)
|
112
160
|
},
|
@@ -115,6 +163,21 @@ module Primer
|
|
115
163
|
}
|
116
164
|
}
|
117
165
|
|
166
|
+
renders_one :segmented_control, lambda { |**system_arguments, &block|
|
167
|
+
deny_tag_argument(**system_arguments)
|
168
|
+
system_arguments[:mr] ||= 2
|
169
|
+
system_arguments = set_as_hidden_filter_target(system_arguments)
|
170
|
+
|
171
|
+
@segmented_control_block = block
|
172
|
+
@mobile_segmented_control = Primer::OpenProject::SubHeader::SegmentedControl.new(
|
173
|
+
hide_labels: true,
|
174
|
+
display: MOBILE_ACTIONS_DISPLAY,
|
175
|
+
**system_arguments
|
176
|
+
)
|
177
|
+
|
178
|
+
Primer::OpenProject::SubHeader::SegmentedControl.new(display: DESKTOP_ACTIONS_DISPLAY, **system_arguments)
|
179
|
+
}
|
180
|
+
|
118
181
|
renders_one :text, lambda { |**system_arguments|
|
119
182
|
system_arguments[:font_weight] ||= :bold
|
120
183
|
|
@@ -138,7 +201,9 @@ module Primer
|
|
138
201
|
|
139
202
|
@filter_container = Primer::BaseComponent.new(tag: :div,
|
140
203
|
classes: "SubHeader-filterContainer",
|
141
|
-
display:
|
204
|
+
display: DESKTOP_ACTIONS_DISPLAY,
|
205
|
+
|
206
|
+
mr: 2,
|
142
207
|
data: { targets: SHOWN_FILTER_TARGET_SELECTOR })
|
143
208
|
|
144
209
|
@system_arguments[:classes] = class_names(
|
@@ -146,6 +211,25 @@ module Primer
|
|
146
211
|
system_arguments[:classes]
|
147
212
|
)
|
148
213
|
end
|
214
|
+
|
215
|
+
def before_render
|
216
|
+
@system_arguments[:classes] = class_names(
|
217
|
+
@system_arguments[:classes],
|
218
|
+
"SubHeader--emptyLeftPane" => !segmented_control? && !filter_button && !filter_input
|
219
|
+
)
|
220
|
+
end
|
221
|
+
|
222
|
+
def set_as_hidden_filter_target(system_arguments)
|
223
|
+
system_arguments[:data] ||= {}
|
224
|
+
system_arguments[:data] = merge_data(
|
225
|
+
system_arguments, {
|
226
|
+
data: {
|
227
|
+
targets: HIDDEN_FILTER_TARGET_SELECTOR,
|
228
|
+
}
|
229
|
+
}
|
230
|
+
)
|
231
|
+
system_arguments
|
232
|
+
end
|
149
233
|
end
|
150
234
|
end
|
151
235
|
end
|
@@ -1,15 +1,12 @@
|
|
1
1
|
<%= render(Primer::OpenProject::SubHeader.new) do |component| %>
|
2
2
|
<% component.with_filter_input(name: "filter", label: "Filter") %>
|
3
3
|
|
4
|
-
<% component.
|
5
|
-
<%=
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
item.with_leading_visual_icon(icon: :log)
|
12
|
-
end
|
13
|
-
end %>
|
4
|
+
<% component.with_action_menu(leading_icon: :plus, trailing_icon: :"triangle-down", label: "Create", button_arguments: { scheme: :primary, "aria-label": "Menu"}) do |menu| %>
|
5
|
+
<%= menu.with_item(label: "Subitem 1") do |item|
|
6
|
+
item.with_leading_visual_icon(icon: :paste)
|
7
|
+
end
|
8
|
+
menu.with_item(label: "Subitem 2") do |item|
|
9
|
+
item.with_leading_visual_icon(icon: :log)
|
10
|
+
end %>
|
14
11
|
<% end %>
|
15
12
|
<% end %>
|
@@ -1,11 +1,17 @@
|
|
1
1
|
<%= render(Primer::OpenProject::SubHeader.new) do |component| %>
|
2
2
|
<% component.with_filter_input(name: "filter", label: "Filter") %>
|
3
3
|
|
4
|
-
<% component.
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
<%
|
4
|
+
<% component.with_action_button_group do |group|
|
5
|
+
group.with_button(icon: :note, "aria-label": "Button 1")
|
6
|
+
group.with_button(icon: :rows, "aria-label": "Button 2")
|
7
|
+
group.with_button(icon: "sort-desc", "aria-label": "Button 3")
|
8
|
+
end %>
|
9
|
+
|
10
|
+
<% component.with_action_button(icon_only: true, leading_icon: :star, label: "Star") do
|
11
|
+
"Star"
|
12
|
+
end %>
|
13
|
+
|
14
|
+
<% component.with_action_button(leading_icon: :plus, label: "Create", scheme: :primary) do
|
15
|
+
"Create"
|
16
|
+
end %>
|
11
17
|
<% end %>
|
@@ -3,6 +3,6 @@
|
|
3
3
|
<% component.with_filter_component do %>
|
4
4
|
<!-- Render any custom filter component that you want -->
|
5
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")) %>
|
6
|
+
<%= render(Primer::Beta::IconButton.new(icon: "filter-remove", "aria-label": "Filter")) %>
|
7
7
|
<% end %>
|
8
8
|
<% end %>
|
@@ -1,12 +1,13 @@
|
|
1
1
|
<%= render(Primer::OpenProject::SubHeader.new) do |component| %>
|
2
2
|
<% component.with_filter_input(name: "filter", label: "Filter") %>
|
3
3
|
|
4
|
-
<% component.
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
<% component.with_text { "Only async dialogs are supported in the SubHeader!" } %>
|
5
|
+
|
6
|
+
<% component.with_action_button(leading_icon: :alert,
|
7
|
+
label: "Open Dialog",
|
8
|
+
#tag: :a,
|
9
|
+
#href: "show_dialog_path",
|
10
|
+
data: { controller: "async-dialog" }) do
|
11
|
+
"Open Dialog"
|
12
|
+
end %>
|
12
13
|
<% end %>
|
@@ -35,9 +35,8 @@ module Primer
|
|
35
35
|
|
36
36
|
component.with_text { text } unless text.nil?
|
37
37
|
|
38
|
-
component.with_action_button(scheme: :primary) do
|
39
|
-
|
40
|
-
"Create"
|
38
|
+
component.with_action_button(leading_icon: :plus, label: "Create", scheme: :primary) do
|
39
|
+
"Create"
|
41
40
|
end if show_action_button
|
42
41
|
end
|
43
42
|
end
|
@@ -51,8 +50,7 @@ module Primer
|
|
51
50
|
"Filter"
|
52
51
|
end
|
53
52
|
|
54
|
-
component.with_action_button(scheme: :primary)
|
55
|
-
button.with_leading_visual_icon(icon: :plus)
|
53
|
+
component.with_action_button(leading_icon: :plus, label: "Create", scheme: :primary) do
|
56
54
|
"Create"
|
57
55
|
end
|
58
56
|
end
|
@@ -64,6 +62,9 @@ module Primer
|
|
64
62
|
end
|
65
63
|
|
66
64
|
# @label With Dialog
|
65
|
+
# Only async dialogs are supported in the SubHeader.
|
66
|
+
# Since we duplicate the buttons for mobile purposes,
|
67
|
+
# the dialog would otherwise be duplicated (with the same ID) as well
|
67
68
|
def dialog_buttons
|
68
69
|
render_with_template(locals: {})
|
69
70
|
end
|
@@ -78,6 +79,25 @@ module Primer
|
|
78
79
|
render_with_template(locals: {})
|
79
80
|
end
|
80
81
|
|
82
|
+
# @label With SegmentedControl
|
83
|
+
def segmented_control
|
84
|
+
render(Primer::OpenProject::SubHeader.new) do |component|
|
85
|
+
component.with_filter_button do |button|
|
86
|
+
button.with_trailing_visual_counter(count: "15")
|
87
|
+
"Filter"
|
88
|
+
end
|
89
|
+
|
90
|
+
component.with_segmented_control("aria-label": "Segmented control") do |control|
|
91
|
+
control.with_item(tag: :a, href: "#", label: "Preview", icon: :eye, selected: true)
|
92
|
+
control.with_item(tag: :a, href: "#", label: "Raw", icon: :"file-code")
|
93
|
+
end
|
94
|
+
|
95
|
+
component.with_action_button(leading_icon: :plus, label: "Create", scheme: :primary) do
|
96
|
+
"Create"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
81
101
|
# @label With a custom area below
|
82
102
|
def bottom_pane
|
83
103
|
render_with_template(locals: {})
|
@@ -90,8 +110,7 @@ module Primer
|
|
90
110
|
|
91
111
|
component.with_text { "Hello world!" }
|
92
112
|
|
93
|
-
component.with_action_button(scheme: :primary)
|
94
|
-
button.with_leading_visual_icon(icon: :plus)
|
113
|
+
component.with_action_button(leading_icon: :plus, label: "Create", scheme: :primary) do
|
95
114
|
"Create"
|
96
115
|
end
|
97
116
|
end
|