openproject-primer_view_components 0.64.1 → 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.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view.d.ts +29 -0
  4. data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_icon_pair_element.d.ts +15 -0
  5. data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_include_fragment_element.d.ts +9 -0
  6. data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_roving_tab_index.d.ts +3 -0
  7. data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.d.ts +38 -0
  8. data/app/assets/javascripts/components/primer/primer.d.ts +4 -0
  9. data/app/assets/javascripts/components/primer/shared_events.d.ts +15 -0
  10. data/app/assets/javascripts/primer_view_components.js +1 -1
  11. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  12. data/app/assets/styles/primer_view_components.css +1 -1
  13. data/app/assets/styles/primer_view_components.css.map +1 -1
  14. data/app/components/primer/alpha/select_panel.css +1 -1
  15. data/app/components/primer/alpha/select_panel.css.json +2 -2
  16. data/app/components/primer/alpha/select_panel.css.map +1 -1
  17. data/app/components/primer/alpha/select_panel.html.erb +1 -1
  18. data/app/components/primer/alpha/select_panel.pcss +5 -2
  19. data/app/components/primer/beta/spinner.html.erb +1 -1
  20. data/app/components/primer/beta/spinner.rb +2 -0
  21. data/app/components/primer/open_project/file_tree_view/directory_node.html.erb +5 -0
  22. data/app/components/primer/open_project/file_tree_view/directory_node.rb +24 -0
  23. data/app/components/primer/open_project/file_tree_view/file_node.html.erb +2 -0
  24. data/app/components/primer/open_project/file_tree_view/file_node.rb +14 -0
  25. data/app/components/primer/open_project/file_tree_view.rb +15 -0
  26. data/app/components/primer/open_project/page_header.rb +1 -1
  27. data/app/components/primer/open_project/skeleton_box.css +1 -0
  28. data/app/components/primer/open_project/skeleton_box.css.json +6 -0
  29. data/app/components/primer/open_project/skeleton_box.css.map +1 -0
  30. data/app/components/primer/open_project/skeleton_box.html.erb +1 -0
  31. data/app/components/primer/open_project/skeleton_box.pcss +30 -0
  32. data/app/components/primer/open_project/skeleton_box.rb +27 -0
  33. data/app/components/primer/open_project/sub_header/button.rb +43 -0
  34. data/app/components/primer/open_project/sub_header/button_group.rb +16 -0
  35. data/app/components/primer/open_project/sub_header/menu.rb +67 -0
  36. data/app/components/primer/open_project/sub_header/segmented_control.rb +16 -0
  37. data/app/components/primer/open_project/sub_header.css +1 -1
  38. data/app/components/primer/open_project/sub_header.css.json +4 -1
  39. data/app/components/primer/open_project/sub_header.css.map +1 -1
  40. data/app/components/primer/open_project/sub_header.html.erb +21 -0
  41. data/app/components/primer/open_project/sub_header.pcss +29 -3
  42. data/app/components/primer/open_project/sub_header.rb +105 -21
  43. data/app/components/primer/open_project/tree_view/icon.html.erb +1 -0
  44. data/app/components/primer/open_project/tree_view/icon.rb +22 -0
  45. data/app/components/primer/open_project/tree_view/icon_pair.html.erb +13 -0
  46. data/app/components/primer/open_project/tree_view/icon_pair.rb +42 -0
  47. data/app/components/primer/open_project/tree_view/leading_action.html.erb +3 -0
  48. data/app/components/primer/open_project/tree_view/leading_action.rb +18 -0
  49. data/app/components/primer/open_project/tree_view/leaf_node.html.erb +18 -0
  50. data/app/components/primer/open_project/tree_view/leaf_node.rb +96 -0
  51. data/app/components/primer/open_project/tree_view/loading_failure_message.html.erb +13 -0
  52. data/app/components/primer/open_project/tree_view/loading_failure_message.rb +31 -0
  53. data/app/components/primer/open_project/tree_view/node.html.erb +32 -0
  54. data/app/components/primer/open_project/tree_view/node.rb +155 -0
  55. data/app/components/primer/open_project/tree_view/skeleton_loader.html.erb +23 -0
  56. data/app/components/primer/open_project/tree_view/skeleton_loader.rb +36 -0
  57. data/app/components/primer/open_project/tree_view/spinner_loader.html.erb +20 -0
  58. data/app/components/primer/open_project/tree_view/spinner_loader.rb +33 -0
  59. data/app/components/primer/open_project/tree_view/sub_tree.html.erb +21 -0
  60. data/app/components/primer/open_project/tree_view/sub_tree.rb +106 -0
  61. data/app/components/primer/open_project/tree_view/sub_tree_container.html.erb +3 -0
  62. data/app/components/primer/open_project/tree_view/sub_tree_container.rb +39 -0
  63. data/app/components/primer/open_project/tree_view/sub_tree_node.html.erb +49 -0
  64. data/app/components/primer/open_project/tree_view/sub_tree_node.rb +172 -0
  65. data/app/components/primer/open_project/tree_view/tree_view.d.ts +29 -0
  66. data/app/components/primer/open_project/tree_view/tree_view.js +238 -0
  67. data/app/components/primer/open_project/tree_view/tree_view.ts +257 -0
  68. data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.d.ts +15 -0
  69. data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.js +62 -0
  70. data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.ts +56 -0
  71. data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.d.ts +9 -0
  72. data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.js +29 -0
  73. data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.ts +29 -0
  74. data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.d.ts +3 -0
  75. data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.js +126 -0
  76. data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.ts +156 -0
  77. data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.d.ts +38 -0
  78. data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.js +362 -0
  79. data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.ts +402 -0
  80. data/app/components/primer/open_project/tree_view/visual.html.erb +14 -0
  81. data/app/components/primer/open_project/tree_view/visual.rb +27 -0
  82. data/app/components/primer/open_project/tree_view.css +1 -0
  83. data/app/components/primer/open_project/tree_view.css.json +42 -0
  84. data/app/components/primer/open_project/tree_view.css.map +1 -0
  85. data/app/components/primer/open_project/tree_view.html.erb +7 -0
  86. data/app/components/primer/open_project/tree_view.pcss +319 -0
  87. data/app/components/primer/open_project/tree_view.rb +367 -0
  88. data/app/components/primer/primer.d.ts +4 -0
  89. data/app/components/primer/primer.js +4 -0
  90. data/app/components/primer/primer.pcss +2 -0
  91. data/app/components/primer/primer.ts +4 -0
  92. data/app/components/primer/shared_events.d.ts +15 -0
  93. data/app/components/primer/shared_events.ts +19 -0
  94. data/app/lib/primer/forms/acts_as_component.rb +1 -12
  95. data/lib/primer/view_components/version.rb +2 -2
  96. data/previews/primer/open_project/file_tree_view_preview/default.html.erb +16 -0
  97. data/previews/primer/open_project/file_tree_view_preview/playground.html.erb +4 -0
  98. data/previews/primer/open_project/file_tree_view_preview.rb +69 -0
  99. data/previews/primer/open_project/page_header_preview/create_action.html.erb +1 -2
  100. data/previews/primer/open_project/skeleton_box_preview.rb +20 -0
  101. data/previews/primer/open_project/sub_header_preview/action_menu_buttons.html.erb +7 -10
  102. data/previews/primer/open_project/sub_header_preview/button_group.html.erb +13 -7
  103. data/previews/primer/open_project/sub_header_preview/custom_filter_button.html.erb +1 -1
  104. data/previews/primer/open_project/sub_header_preview/dialog_buttons.html.erb +9 -8
  105. data/previews/primer/open_project/sub_header_preview.rb +26 -7
  106. data/previews/primer/open_project/tree_view_preview/default.html.erb +24 -0
  107. data/previews/primer/open_project/tree_view_preview/empty.html.erb +10 -0
  108. data/previews/primer/open_project/tree_view_preview/leaf_node_playground.html.erb +15 -0
  109. data/previews/primer/open_project/tree_view_preview/loading_failure.html.erb +36 -0
  110. data/previews/primer/open_project/tree_view_preview/loading_skeleton.html.erb +12 -0
  111. data/previews/primer/open_project/tree_view_preview/loading_spinner.html.erb +12 -0
  112. data/previews/primer/open_project/tree_view_preview/playground.html.erb +4 -0
  113. data/previews/primer/open_project/tree_view_preview.rb +139 -0
  114. data/static/arguments.json +512 -0
  115. data/static/audited_at.json +21 -0
  116. data/static/classes.json +18 -0
  117. data/static/constants.json +108 -1
  118. data/static/info_arch.json +1612 -0
  119. data/static/previews.json +180 -0
  120. data/static/statuses.json +21 -0
  121. metadata +79 -2
@@ -1 +1 @@
1
- .SelectPanel-emptyPanel,.SelectPanel-loadingPanel{min-height:min(var(--overlay-height) - 10rem,100vh - 2rem)}
1
+ .SelectPanel-loadingPanel{min-height:min(calc(var(--overlay-height) - 10rem),calc(100vh - 2rem))}.SelectPanel-emptyPanel{align-items:center;display:flex;justify-content:center;min-height:min(calc(var(--overlay-height) - 23rem),calc(100vh - 2rem))}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "alpha/select_panel",
3
3
  "selectors": [
4
- ".SelectPanel-emptyPanel",
5
- ".SelectPanel-loadingPanel"
4
+ ".SelectPanel-loadingPanel",
5
+ ".SelectPanel-emptyPanel"
6
6
  ]
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["select_panel.pcss"],"names":[],"mappings":"AAIA,kDACE,0DACF","file":"select_panel.css","sourcesContent":[".SelectPanel-loadingPanel {\n min-height: min(var(--overlay-height) - 10rem, 100vh - 2rem);\n}\n\n.SelectPanel-emptyPanel {\n min-height: min(var(--overlay-height) - 10rem, 100vh - 2rem);\n}\n"]}
1
+ {"version":3,"sources":["select_panel.pcss"],"names":[],"mappings":"AAAA,0BACE,sEACF,CAEA,wBAEE,kBAAmB,CACnB,YAAa,CACb,sBAAuB,CAHvB,sEAIF","file":"select_panel.css","sourcesContent":[".SelectPanel-loadingPanel {\n min-height: min(calc(var(--overlay-height) - 10rem), calc(100vh - 2rem));\n}\n\n.SelectPanel-emptyPanel {\n min-height: min(calc(var(--overlay-height) - 23rem), calc(100vh - 2rem));\n align-items: center;\n display: flex;\n justify-content: center;\n}\n"]}
@@ -97,7 +97,7 @@
97
97
  <%= render(@list) %>
98
98
  <% end %>
99
99
  </div>
100
- <div data-target="select-panel.noResults" class="pt-2 color-border-muted text-center d-flex flex-items-start flex-justify-center SelectPanel-emptyPanel" hidden>
100
+ <div data-target="select-panel.noResults" class="SelectPanel-emptyPanel" hidden>
101
101
  <h2 class="v-align-middle m-3 f5"><%= @no_results_label %></h2>
102
102
  </div>
103
103
  <% end %>
@@ -1,7 +1,10 @@
1
1
  .SelectPanel-loadingPanel {
2
- min-height: min(var(--overlay-height) - 10rem, 100vh - 2rem);
2
+ min-height: min(calc(var(--overlay-height) - 10rem), calc(100vh - 2rem));
3
3
  }
4
4
 
5
5
  .SelectPanel-emptyPanel {
6
- min-height: min(var(--overlay-height) - 10rem, 100vh - 2rem);
6
+ min-height: min(calc(var(--overlay-height) - 23rem), calc(100vh - 2rem));
7
+ align-items: center;
8
+ display: flex;
9
+ justify-content: center;
7
10
  }
@@ -1,4 +1,4 @@
1
- <%= render(Primer::BaseComponent.new(tag: :span, hidden: @hidden, data: { target: @target })) do %>
1
+ <%= render(Primer::BaseComponent.new(tag: :span, hidden: @hidden, data: { target: @target }, **@wrapper_arguments)) do %>
2
2
  <%= render Primer::BaseComponent.new(**@system_arguments) do %>
3
3
  <circle cx="8" cy="8" r="7" stroke="currentColor" stroke-opacity="0.25" stroke-width="2" vector-effect="non-scaling-stroke" fill="none" />
4
4
  <path d="M15 8a7.002 7.002 0 00-7-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" vector-effect="non-scaling-stroke" />
@@ -43,6 +43,8 @@ module Primer
43
43
 
44
44
  @target = extract_data(:target, @system_arguments)
45
45
  @hidden = @system_arguments.delete(:hidden)
46
+
47
+ @wrapper_arguments = @system_arguments.delete(:wrapper_arguments) || {}
46
48
  end
47
49
 
48
50
  def no_aria_label?
@@ -0,0 +1,5 @@
1
+ <% with_leading_visual_icons do |icons| %>
2
+ <% icons.with_expanded_icon(icon: :"file-directory-open-fill", **@expanded_icon_arguments) %>
3
+ <% icons.with_collapsed_icon(icon: :"file-directory-fill", **@collapsed_icon_arguments) %>
4
+ <% end %>
5
+ <%= render_parent %>
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module OpenProject
5
+ class FileTreeView
6
+ class DirectoryNode < TreeView::SubTreeNode
7
+ def initialize(icon_arguments: nil, expanded_icon_arguments: nil, collapsed_icon_arguments: nil, **system_arguments)
8
+ @expanded_icon_arguments = expanded_icon_arguments || icon_arguments || {}
9
+ @collapsed_icon_arguments = collapsed_icon_arguments || icon_arguments || {}
10
+
11
+ super(**system_arguments)
12
+ end
13
+
14
+ def with_file(**system_arguments, &block)
15
+ with_leaf(**system_arguments, component_klass: FileNode, &block)
16
+ end
17
+
18
+ def with_directory(**system_arguments, &block)
19
+ with_sub_tree(**system_arguments, component_klass: DirectoryNode, &block)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,2 @@
1
+ <% with_leading_visual_icon(icon: :file, **@icon_arguments) %>
2
+ <%= render_parent %>
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module OpenProject
5
+ class FileTreeView
6
+ class FileNode < TreeView::LeafNode
7
+ def initialize(icon_arguments: {}, **system_arguments)
8
+ @icon_arguments = icon_arguments
9
+ super(**system_arguments)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module OpenProject
5
+ class FileTreeView < TreeView
6
+ def with_file(**system_arguments, &block)
7
+ with_leaf(**system_arguments, component_klass: FileNode, &block)
8
+ end
9
+
10
+ def with_directory(**system_arguments, &block)
11
+ with_sub_tree(**system_arguments, component_klass: DirectoryNode, &block)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -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: %i[flex none])
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 @@
1
+ @keyframes shimmer{0%{mask-position:200%}to{mask-position:0}}.SkeletonBox{animation:shimmer;background-color:var(--bgColor-muted);border-radius:var(--borderRadius-small);display:block;height:1rem}@media (prefers-reduced-motion:no-preference){.SkeletonBox{animation:shimmer;animation-duration:1s;animation-iteration-count:infinite;mask-image:linear-gradient(75deg,#000 30%,#000000a6 80%);mask-size:200%}}@media (forced-colors:active){.SkeletonBox{outline:1px solid #0000;outline-offset:-1px}}
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "open_project/skeleton_box",
3
+ "selectors": [
4
+ ".SkeletonBox"
5
+ ]
6
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["skeleton_box.pcss"],"names":[],"mappings":"AAAA,mBACE,GACE,kBACF,CAEA,GACE,eACF,CACF,CAEA,aAKE,iBAAkB,CAFlB,qCAAsC,CACtC,uCAAwC,CAHxC,aAAc,CACd,WAiBF,CAZE,8CAPF,aAUI,iBAAkB,CAClB,qBAAsB,CACtB,kCAAmC,CAJnC,wDAAqE,CACrE,cAUJ,CANE,CAEA,8BAfF,aAgBI,uBAA8B,CAC9B,mBAEJ,CADE","file":"skeleton_box.css","sourcesContent":["@keyframes shimmer {\n from {\n mask-position: 200%;\n }\n\n to {\n mask-position: 0%;\n }\n}\n\n.SkeletonBox {\n display: block;\n height: 1rem;\n background-color: var(--bgColor-muted);\n border-radius: var(--borderRadius-small);\n animation: shimmer;\n\n @media (prefers-reduced-motion: no-preference) {\n mask-image: linear-gradient(75deg, #000 30%, rgba(0, 0, 0, 0.65) 80%);\n mask-size: 200%;\n animation: shimmer;\n animation-duration: 1s;\n animation-iteration-count: infinite;\n }\n\n @media (forced-colors: active) {\n outline: 1px solid transparent;\n outline-offset: -1px;\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ <%= render(Primer::BaseComponent.new(**@system_arguments)) %>
@@ -0,0 +1,30 @@
1
+ @keyframes shimmer {
2
+ from {
3
+ mask-position: 200%;
4
+ }
5
+
6
+ to {
7
+ mask-position: 0%;
8
+ }
9
+ }
10
+
11
+ .SkeletonBox {
12
+ display: block;
13
+ height: 1rem;
14
+ background-color: var(--bgColor-muted);
15
+ border-radius: var(--borderRadius-small);
16
+ animation: shimmer;
17
+
18
+ @media (prefers-reduced-motion: no-preference) {
19
+ mask-image: linear-gradient(75deg, #000 30%, rgba(0, 0, 0, 0.65) 80%);
20
+ mask-size: 200%;
21
+ animation: shimmer;
22
+ animation-duration: 1s;
23
+ animation-iteration-count: infinite;
24
+ }
25
+
26
+ @media (forced-colors: active) {
27
+ outline: 1px solid transparent;
28
+ outline-offset: -1px;
29
+ }
30
+ }
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module OpenProject
5
+ # A SkeletonBox provides a placeholder for non-text, non-Avatar elements (e.g., hero images)
6
+ # that are still loading. You can adjust width and height to match the content's dimensions.
7
+ class SkeletonBox < Primer::Component
8
+ # @param height [String] Any valid CSS height.
9
+ # @param width [String] Any valid CSS width.
10
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
11
+ def initialize(height: nil, width: nil, **system_arguments)
12
+ @system_arguments = deny_tag_argument(**system_arguments)
13
+
14
+ styles = []
15
+ styles << "width: #{width}" if width
16
+ styles << "height: #{height}" if height
17
+
18
+ @system_arguments[:tag] = :div
19
+ @system_arguments[:style] = styles.join("; ") if styles.present?
20
+ @system_arguments[:classes] = class_names(
21
+ @system_arguments.delete(:classes),
22
+ "SkeletonBox"
23
+ )
24
+ end
25
+ end
26
+ end
27
+ end
@@ -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:16px}.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;gap:12px;grid-area:left;width:100%}:is(.SubHeader-leftPane [class*=FormControl-input-width--]):not(.FormControl-input-width--auto){width:100vw}.SubHeader-filterContainer{display:flex;gap:8px;width:100%}.SubHeader-filterInput_hiddenClearButton+button.FormControl-input-trailingAction{display:none}
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+button.FormControl-input-trailingAction"
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,kBACJ,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,CAGb,QAAS,CAJT,cAAe,CAGf,UAUJ,CAJQ,gGACI,WACJ,CAIR,2BACI,YAAa,CAEb,OAAQ,CADR,UAEJ,CAEA,iFACE,YACF","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: 16px;\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 gap: 12px;\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 width: 100%;\n gap: 8px;\n}\n\n.SubHeader-filterInput_hiddenClearButton + button.FormControl-input-trailingAction {\n display: none;\n}\n"]}
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: 16px;
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 + button.FormControl-input-trailingAction {
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
+ }