primer_view_components 0.3.1 → 0.4.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 +16 -0
- data/app/assets/javascripts/app/components/primer/alpha/action_bar_element.d.ts +16 -0
- data/app/assets/javascripts/app/components/primer/alpha/action_menu/action_menu_element.d.ts +30 -0
- data/app/assets/javascripts/app/components/primer/alpha/dropdown/menu.d.ts +1 -0
- data/app/assets/javascripts/app/components/primer/alpha/dropdown.d.ts +1 -0
- data/app/assets/javascripts/app/components/primer/alpha/image_crop.d.ts +1 -0
- data/app/assets/javascripts/app/components/primer/alpha/modal_dialog.d.ts +18 -0
- data/app/assets/javascripts/app/components/primer/alpha/nav_list.d.ts +28 -0
- data/app/assets/javascripts/app/components/primer/alpha/segmented_control.d.ts +12 -0
- data/app/assets/javascripts/app/components/primer/alpha/tab_container.d.ts +1 -0
- data/app/assets/javascripts/app/components/primer/alpha/toggle_switch.d.ts +30 -0
- data/app/assets/javascripts/app/components/primer/alpha/tool_tip.d.ts +26 -0
- data/app/assets/javascripts/app/components/primer/alpha/x_banner.d.ts +11 -0
- data/app/assets/javascripts/app/components/primer/anchored_position.d.ts +27 -0
- data/app/assets/javascripts/app/components/primer/beta/auto_complete/auto_complete.d.ts +1 -0
- data/app/assets/javascripts/app/components/primer/beta/clipboard_copy.d.ts +1 -0
- data/app/assets/javascripts/app/components/primer/beta/relative_time.d.ts +1 -0
- data/app/assets/javascripts/app/components/primer/focus_group.d.ts +19 -0
- data/app/assets/javascripts/app/components/primer/primer.d.ts +21 -0
- data/app/assets/javascripts/lib/primer/forms/primer_multi_input.d.ts +10 -0
- data/app/assets/javascripts/lib/primer/forms/primer_text_field.d.ts +1 -0
- data/app/assets/javascripts/lib/primer/forms/toggle_switch_input.d.ts +5 -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/alpha/action_bar/divider.rb +30 -0
- data/app/components/primer/alpha/action_bar/item.rb +26 -0
- data/app/components/primer/alpha/action_bar.css +1 -0
- data/app/components/primer/alpha/action_bar.css.json +17 -0
- data/app/components/primer/alpha/action_bar.css.map +1 -0
- data/app/components/primer/alpha/action_bar.html.erb +12 -0
- data/app/components/primer/alpha/action_bar.pcss +69 -0
- data/app/components/primer/alpha/action_bar.rb +110 -0
- data/app/components/primer/alpha/action_bar_element.d.ts +16 -0
- data/app/components/primer/alpha/action_bar_element.js +172 -0
- data/app/components/primer/alpha/action_bar_element.ts +175 -0
- data/app/components/primer/alpha/action_menu/action_menu_element.d.ts +2 -2
- data/app/components/primer/alpha/tool_tip.d.ts +3 -2
- data/app/components/primer/alpha/tool_tip.js +89 -44
- data/app/components/primer/alpha/tool_tip.ts +88 -41
- data/app/components/primer/alpha/tooltip.rb +1 -0
- data/app/components/primer/beta/link.css +1 -1
- data/app/components/primer/beta/link.css.map +1 -1
- data/app/components/primer/beta/link.pcss +4 -0
- data/app/components/primer/beta/link.rb +6 -10
- 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/static/generate_previews.rb +9 -0
- data/lib/primer/view_components/linters/tooltipped_migration.rb +1 -3
- data/lib/primer/view_components/version.rb +2 -2
- data/lib/primer/yard.rb +5 -0
- data/previews/primer/alpha/action_bar_preview/inline.html.erb +16 -0
- data/previews/primer/alpha/action_bar_preview.rb +77 -0
- data/previews/primer/alpha/action_list_preview.rb +10 -0
- data/previews/primer/alpha/action_menu_preview.rb +5 -0
- data/previews/primer/alpha/auto_complete_preview.rb +1 -0
- data/previews/primer/alpha/banner_preview.rb +9 -1
- data/previews/primer/alpha/button_marketing_preview.rb +2 -0
- data/previews/primer/alpha/check_box_group_preview.rb +1 -0
- data/previews/primer/alpha/check_box_preview.rb +6 -0
- data/previews/primer/alpha/dialog_preview.rb +1 -0
- data/previews/primer/alpha/dropdown_preview.rb +1 -0
- data/previews/primer/alpha/hellip_button_preview.rb +1 -0
- data/previews/primer/alpha/hidden_text_expander_preview.rb +1 -0
- data/previews/primer/alpha/layout_preview.rb +4 -0
- data/previews/primer/alpha/menu_preview.rb +1 -0
- data/previews/primer/alpha/multi_input_preview.rb +4 -0
- data/previews/primer/alpha/nav_list_preview.rb +3 -0
- data/previews/primer/alpha/radio_button_group_preview.rb +2 -0
- data/previews/primer/alpha/radio_button_preview.rb +10 -0
- data/previews/primer/alpha/segmented_control_preview.rb +13 -0
- data/previews/primer/alpha/select_preview.rb +6 -0
- data/previews/primer/alpha/tab_nav_preview.rb +3 -0
- data/previews/primer/alpha/tab_panels_preview.rb +1 -0
- data/previews/primer/alpha/text_area_preview.rb +7 -0
- data/previews/primer/alpha/text_field_preview.rb +15 -0
- data/previews/primer/alpha/toggle_switch_preview.rb +7 -0
- data/previews/primer/alpha/tooltip_preview/tooltip_inside_primer_overlay.html.erb +20 -0
- data/previews/primer/alpha/tooltip_preview.rb +7 -0
- data/previews/primer/alpha/underline_nav_preview.rb +2 -0
- data/previews/primer/beta/auto_complete_item_preview.rb +2 -0
- data/previews/primer/beta/auto_complete_preview.rb +7 -0
- data/previews/primer/beta/avatar_preview.rb +10 -0
- data/previews/primer/beta/avatar_stack_preview.rb +3 -0
- data/previews/primer/beta/blankslate_preview.rb +9 -0
- data/previews/primer/beta/border_box_preview.rb +4 -0
- data/previews/primer/beta/breadcrumbs_preview.rb +1 -0
- data/previews/primer/beta/button_group_preview.rb +4 -0
- data/previews/primer/beta/button_preview.rb +10 -0
- data/previews/primer/beta/clipboard_copy_preview.rb +2 -0
- data/previews/primer/beta/close_button_preview.rb +1 -0
- data/previews/primer/beta/counter_preview.rb +11 -0
- data/previews/primer/beta/flash_preview.rb +8 -0
- data/previews/primer/beta/heading_preview.rb +1 -0
- data/previews/primer/beta/icon_button_preview.rb +3 -0
- data/previews/primer/beta/label_preview.rb +13 -0
- data/previews/primer/beta/link_preview.rb +11 -9
- data/previews/primer/beta/markdown_preview.rb +1 -0
- data/previews/primer/beta/octicon_preview.rb +1 -0
- data/previews/primer/beta/popover_preview.rb +6 -0
- data/previews/primer/beta/progress_bar_preview.rb +4 -0
- data/previews/primer/beta/spinner_preview.rb +1 -0
- data/previews/primer/beta/state_preview.rb +6 -0
- data/previews/primer/beta/subhead_preview.rb +4 -0
- data/previews/primer/beta/text_preview.rb +1 -0
- data/previews/primer/beta/timeline_item_preview.rb +1 -0
- data/previews/primer/beta/truncate_preview.rb +1 -0
- data/previews/primer/box_preview.rb +2 -0
- data/static/arguments.json +51 -7
- data/static/audited_at.json +3 -0
- data/static/classes.json +21 -0
- data/static/constants.json +20 -6
- data/static/info_arch.json +669 -7
- data/static/previews.json +571 -0
- data/static/statuses.json +3 -0
- metadata +43 -8
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :nocov:
|
4
|
+
|
5
|
+
module Primer
|
6
|
+
module Alpha
|
7
|
+
class ActionBar
|
8
|
+
# ActionBar::Item is an internal component that wraps the items in a div with the `ActionBar-item` class.
|
9
|
+
class Divider < Primer::Component
|
10
|
+
def initialize
|
11
|
+
@system_arguments = {
|
12
|
+
tag: :hr,
|
13
|
+
role: :presentation,
|
14
|
+
aria: {
|
15
|
+
hidden: true
|
16
|
+
},
|
17
|
+
data: {
|
18
|
+
targets: "action-bar.items"
|
19
|
+
},
|
20
|
+
classes: "ActionBar-item ActionBar-divider"
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def call
|
25
|
+
render(Primer::BaseComponent.new(**@system_arguments)) { content }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :nocov:
|
4
|
+
|
5
|
+
module Primer
|
6
|
+
module Alpha
|
7
|
+
class ActionBar
|
8
|
+
# ActionBar::Item is an internal component that wraps the items in a div with the `ActionBar-item` class.
|
9
|
+
class Item < Primer::Component
|
10
|
+
def initialize
|
11
|
+
@system_arguments = {
|
12
|
+
tag: :div,
|
13
|
+
data: {
|
14
|
+
targets: "action-bar.items"
|
15
|
+
},
|
16
|
+
classes: "ActionBar-item"
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
render(Primer::BaseComponent.new(**@system_arguments)) { content }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
.ActionBar{display:flex!important;flex-grow:1;flex-shrink:1;justify-content:flex-end;min-width:calc(var(--control-medium-size,2rem)*3);overflow:hidden;position:relative}.ActionBar,.ActionBar-item-container{align-items:center;box-sizing:initial}.ActionBar-item-container{display:flex;flex-grow:0;flex-shrink:0}.ActionBar-item{flex-shrink:0;position:relative}.ActionBar-more-menu{flex-shrink:0}.ActionBar--small{min-width:calc(var(--control-small-size,1.75rem)*3)}.ActionBar--large{min-width:calc(var(--control-large-size,2.5rem)*3)}.ActionBar-divider{border-left:var(--borderWidth-thin,max(1px,.0625rem)) solid var(--color-border-subtle);height:calc(var(--control-medium-size,2rem)/2);margin:0 var(--controlStack-medium-gap-condensed,.5rem)}.ActionBar--small .ActionBar-divider{margin:0 var(--controlStack-small-gap-condensed,.5rem)}.ActionBar--large .ActionBar-divider{margin:0 var(--controlStack-large-gap-condensed,.5rem)}@media (pointer:coarse){.ActionBar .ActionBar-item-container{gap:calc(var(--control-minTarget-coarse,2.75rem) - var(--control-medium-size,2rem))}.ActionBar--small .ActionBar-item-container{gap:calc(var(--control-minTarget-coarse,2.75rem) - var(--control-small-size,1.75rem))}.ActionBar--large .ActionBar-item-container{gap:calc(var(--control-minTarget-coarse,2.75rem) - var(--control-large-size,2.5rem))}}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
{
|
2
|
+
"name": "alpha/action_bar",
|
3
|
+
"selectors": [
|
4
|
+
".ActionBar",
|
5
|
+
".ActionBar-item-container",
|
6
|
+
".ActionBar-item",
|
7
|
+
".ActionBar-more-menu",
|
8
|
+
".ActionBar--small",
|
9
|
+
".ActionBar--large",
|
10
|
+
".ActionBar-divider",
|
11
|
+
".ActionBar--small .ActionBar-divider",
|
12
|
+
".ActionBar--large .ActionBar-divider",
|
13
|
+
".ActionBar .ActionBar-item-container",
|
14
|
+
".ActionBar--small .ActionBar-item-container",
|
15
|
+
".ActionBar--large .ActionBar-item-container"
|
16
|
+
]
|
17
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["action_bar.pcss"],"names":[],"mappings":"AACA,WAEE,sBAAwB,CAGxB,WAAY,CACZ,aAAc,CAGd,wBAAyB,CANzB,iDAA+C,CAK/C,eAAgB,CAPhB,iBASF,CAEA,qCARE,kBAAmB,CAGnB,kBAWF,CANA,0BACE,YAAa,CAIb,WAAY,CADZ,aAEF,CAEA,gBAEE,aAAc,CADd,iBAEF,CAEA,qBACE,aACF,CAEA,kBACE,mDACF,CAEA,kBACE,kDACF,CAIA,mBAGE,sFAAqE,CAFrE,8CAA4C,CAC5C,uDAEF,CAEA,qCACE,sDACF,CAEA,qCACE,sDACF,CAIA,wBACE,qCACE,mFACF,CAEA,4CACE,qFACF,CAEA,4CACE,oFACF,CACF","file":"action_bar.css","sourcesContent":["/* CSS for ActionBar */\n.ActionBar {\n position: relative;\n display: flex !important;\n min-width: calc(var(--control-medium-size) * 3); \n align-items: center;\n flex-grow: 1;\n flex-shrink: 1;\n box-sizing: content-box;\n overflow: hidden;\n justify-content: flex-end;\n}\n\n.ActionBar-item-container {\n display: flex;\n box-sizing: content-box;\n align-items: center;\n flex-shrink: 0;\n flex-grow: 0;\n}\n\n.ActionBar-item {\n position: relative;\n flex-shrink: 0;\n}\n\n.ActionBar-more-menu {\n flex-shrink: 0;\n}\n\n.ActionBar--small {\n min-width: calc(var(--control-small-size) * 3);\n}\n\n.ActionBar--large {\n min-width: calc(var(--control-large-size) * 3);\n}\n\n/* Divider */\n\n.ActionBar-divider {\n height: calc(var(--control-medium-size) / 2);\n margin: 0 var(--controlStack-medium-gap-condensed);\n border-left: var(--borderWidth-thin) solid var(--color-border-subtle);\n}\n\n.ActionBar--small .ActionBar-divider {\n margin: 0 var(--controlStack-small-gap-condensed);\n}\n\n.ActionBar--large .ActionBar-divider {\n margin: 0 var(--controlStack-large-gap-condensed);\n}\n\n/* Increase spacing so touch targets don't overlap */\n\n@media (pointer: coarse) {\n .ActionBar .ActionBar-item-container {\n gap: calc(var(--control-minTarget-coarse) - var(--control-medium-size)); /* 12px */\n }\n\n .ActionBar--small .ActionBar-item-container {\n gap: calc(var(--control-minTarget-coarse) - var(--control-small-size)); /* 16px */\n }\n\n .ActionBar--large .ActionBar-item-container {\n gap: calc(var(--control-minTarget-coarse) - var(--control-large-size)); /* 4px */\n }\n}\n"]}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%= render Primer::BaseComponent.new(**@system_arguments) do %>
|
2
|
+
<%= render Primer::BaseComponent.new(tag: :div, classes: "ActionBar-item-container", "data-target": "action-bar.itemContainer") do %>
|
3
|
+
<% items.each do |item| %>
|
4
|
+
<%= item %>
|
5
|
+
<% end %>
|
6
|
+
<% end %>
|
7
|
+
<% if render_overflow_menu? %>
|
8
|
+
<%= render(@action_menu) do |menu| %>
|
9
|
+
<% menu.with_show_button(icon: :"kebab-horizontal", "aria-label": "Menu", size: @size, scheme: :invisible) %>
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
12
|
+
<% end %>
|
@@ -0,0 +1,69 @@
|
|
1
|
+
/* CSS for ActionBar */
|
2
|
+
.ActionBar {
|
3
|
+
position: relative;
|
4
|
+
display: flex !important;
|
5
|
+
min-width: calc(var(--control-medium-size) * 3);
|
6
|
+
align-items: center;
|
7
|
+
flex-grow: 1;
|
8
|
+
flex-shrink: 1;
|
9
|
+
box-sizing: content-box;
|
10
|
+
overflow: hidden;
|
11
|
+
justify-content: flex-end;
|
12
|
+
}
|
13
|
+
|
14
|
+
.ActionBar-item-container {
|
15
|
+
display: flex;
|
16
|
+
box-sizing: content-box;
|
17
|
+
align-items: center;
|
18
|
+
flex-shrink: 0;
|
19
|
+
flex-grow: 0;
|
20
|
+
}
|
21
|
+
|
22
|
+
.ActionBar-item {
|
23
|
+
position: relative;
|
24
|
+
flex-shrink: 0;
|
25
|
+
}
|
26
|
+
|
27
|
+
.ActionBar-more-menu {
|
28
|
+
flex-shrink: 0;
|
29
|
+
}
|
30
|
+
|
31
|
+
.ActionBar--small {
|
32
|
+
min-width: calc(var(--control-small-size) * 3);
|
33
|
+
}
|
34
|
+
|
35
|
+
.ActionBar--large {
|
36
|
+
min-width: calc(var(--control-large-size) * 3);
|
37
|
+
}
|
38
|
+
|
39
|
+
/* Divider */
|
40
|
+
|
41
|
+
.ActionBar-divider {
|
42
|
+
height: calc(var(--control-medium-size) / 2);
|
43
|
+
margin: 0 var(--controlStack-medium-gap-condensed);
|
44
|
+
border-left: var(--borderWidth-thin) solid var(--color-border-subtle);
|
45
|
+
}
|
46
|
+
|
47
|
+
.ActionBar--small .ActionBar-divider {
|
48
|
+
margin: 0 var(--controlStack-small-gap-condensed);
|
49
|
+
}
|
50
|
+
|
51
|
+
.ActionBar--large .ActionBar-divider {
|
52
|
+
margin: 0 var(--controlStack-large-gap-condensed);
|
53
|
+
}
|
54
|
+
|
55
|
+
/* Increase spacing so touch targets don't overlap */
|
56
|
+
|
57
|
+
@media (pointer: coarse) {
|
58
|
+
.ActionBar .ActionBar-item-container {
|
59
|
+
gap: calc(var(--control-minTarget-coarse) - var(--control-medium-size)); /* 12px */
|
60
|
+
}
|
61
|
+
|
62
|
+
.ActionBar--small .ActionBar-item-container {
|
63
|
+
gap: calc(var(--control-minTarget-coarse) - var(--control-small-size)); /* 16px */
|
64
|
+
}
|
65
|
+
|
66
|
+
.ActionBar--large .ActionBar-item-container {
|
67
|
+
gap: calc(var(--control-minTarget-coarse) - var(--control-large-size)); /* 4px */
|
68
|
+
}
|
69
|
+
}
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Alpha
|
5
|
+
# Add a general description of component here
|
6
|
+
# Add additional usage considerations or best practices that may aid the user to use the component correctly.
|
7
|
+
# @accessibility Add any accessibility considerations
|
8
|
+
class ActionBar < Primer::Component
|
9
|
+
status :alpha
|
10
|
+
|
11
|
+
DEFAULT_SIZE = :medium
|
12
|
+
SIZE_MAPPINGS = {
|
13
|
+
DEFAULT_SIZE => nil,
|
14
|
+
:small => "ActionBar--small",
|
15
|
+
:large => "ActionBar--large"
|
16
|
+
}.freeze
|
17
|
+
SIZE_OPTIONS = SIZE_MAPPINGS.keys.freeze
|
18
|
+
|
19
|
+
renders_many :items, types: {
|
20
|
+
icon_button: lambda { |icon:, label:, **system_arguments|
|
21
|
+
item_id = self.class.generate_id
|
22
|
+
|
23
|
+
with_menu_item(id: item_id, label: label) do |c|
|
24
|
+
c.with_leading_visual_icon(icon: icon)
|
25
|
+
end
|
26
|
+
|
27
|
+
render(Item.new) do
|
28
|
+
render(Primer::Beta::IconButton.new(id: item_id, icon: icon, "aria-label": label, size: @size, scheme: :invisible, **system_arguments))
|
29
|
+
end
|
30
|
+
},
|
31
|
+
divider: lambda {
|
32
|
+
@action_menu.with_divider(hidden: true) if @overflow_menu
|
33
|
+
Divider.new
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
# @example Default
|
38
|
+
#
|
39
|
+
# <%= render(Primer::Alpha::ActionBar.new) do |component| %>
|
40
|
+
# <% component.with_item_icon_button(icon: :search, label: "Search")
|
41
|
+
# <% component.with_item_icon_button(icon: :pencil, label: "Edit") %>
|
42
|
+
# <% end %>
|
43
|
+
#
|
44
|
+
# @param size [Symbol] <%= one_of(Primer::Alpha::ActionBar::SIZE_OPTIONS) %>
|
45
|
+
# @param overflow_menu [Boolean] Whether to render the overflow menu.
|
46
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
47
|
+
def initialize(size: Primer::Alpha::ActionBar::DEFAULT_SIZE, overflow_menu: true, **system_arguments)
|
48
|
+
@system_arguments = system_arguments
|
49
|
+
@overflow_menu = overflow_menu
|
50
|
+
|
51
|
+
@size = fetch_or_fallback(Primer::Alpha::ActionBar::SIZE_OPTIONS, size, Primer::Alpha::ActionBar::DEFAULT_SIZE)
|
52
|
+
|
53
|
+
@system_arguments[:classes] = class_names(
|
54
|
+
system_arguments[:classes],
|
55
|
+
"ActionBar",
|
56
|
+
SIZE_MAPPINGS[@size]
|
57
|
+
)
|
58
|
+
@system_arguments[:role] = "toolbar"
|
59
|
+
|
60
|
+
return unless overflow_menu
|
61
|
+
|
62
|
+
@action_menu = Primer::Alpha::ActionMenu.new(
|
63
|
+
menu_id: self.class.generate_id,
|
64
|
+
"data-target": "action-bar.moreMenu",
|
65
|
+
hidden: true,
|
66
|
+
classes: "ActionBar-more-menu",
|
67
|
+
anchor_align: :end
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def with_menu_item(id:, **system_arguments, &block)
|
74
|
+
return unless @overflow_menu
|
75
|
+
|
76
|
+
system_arguments = {
|
77
|
+
**system_arguments,
|
78
|
+
hidden: true,
|
79
|
+
tag: :button,
|
80
|
+
type: "button",
|
81
|
+
"data-for": id,
|
82
|
+
"data-action": "click:action-bar#menuItemClick"
|
83
|
+
}
|
84
|
+
|
85
|
+
@action_menu.with_item(
|
86
|
+
value: "",
|
87
|
+
**system_arguments,
|
88
|
+
&block
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
def render?
|
93
|
+
items.any?
|
94
|
+
end
|
95
|
+
|
96
|
+
def render_overflow_menu?
|
97
|
+
@overflow_menu && items.count > 3
|
98
|
+
end
|
99
|
+
|
100
|
+
def before_render
|
101
|
+
@system_arguments[:tag] = render_overflow_menu? ? :"action-bar" : :div
|
102
|
+
@system_arguments[:classes] = class_names(
|
103
|
+
@system_arguments[:classes],
|
104
|
+
"overflow-visible": !render_overflow_menu?
|
105
|
+
)
|
106
|
+
content
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
declare class ActionBarElement extends HTMLElement {
|
2
|
+
#private;
|
3
|
+
items: HTMLElement[];
|
4
|
+
itemContainer: HTMLElement;
|
5
|
+
moreMenu: HTMLElement;
|
6
|
+
connectedCallback(): void;
|
7
|
+
disconnectedCallback(): void;
|
8
|
+
menuItemClick(event: Event): void;
|
9
|
+
update(rect?: DOMRect): void;
|
10
|
+
}
|
11
|
+
declare global {
|
12
|
+
interface Window {
|
13
|
+
ActionBarElement: typeof ActionBarElement;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
export {};
|
@@ -0,0 +1,172 @@
|
|
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
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
8
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
11
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
12
|
+
};
|
13
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
14
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
15
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
16
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
17
|
+
};
|
18
|
+
var _ActionBarElement_instances, _ActionBarElement_initialBarWidth, _ActionBarElement_previousBarWidth, _ActionBarElement_focusZoneAbortController, _ActionBarElement_itemGap, _ActionBarElement_availableSpace, _ActionBarElement_menuSpace, _ActionBarElement_shrink, _ActionBarElement_grow, _ActionBarElement_showItem, _ActionBarElement_hideItem, _ActionBarElement_menuItems_get;
|
19
|
+
import { controller, targets, target } from '@github/catalyst';
|
20
|
+
import { focusZone, FocusKeys } from '@primer/behaviors';
|
21
|
+
const instersectionObserver = new IntersectionObserver(entries => {
|
22
|
+
for (const entry of entries) {
|
23
|
+
const action = entry.target;
|
24
|
+
if (entry.isIntersecting && action instanceof ActionBarElement) {
|
25
|
+
action.update();
|
26
|
+
}
|
27
|
+
}
|
28
|
+
});
|
29
|
+
const resizeObserver = new ResizeObserver(entries => {
|
30
|
+
for (const entry of entries) {
|
31
|
+
const action = entry.target;
|
32
|
+
if (action instanceof ActionBarElement) {
|
33
|
+
action.update(entry.contentRect);
|
34
|
+
}
|
35
|
+
}
|
36
|
+
});
|
37
|
+
let ActionBarElement = class ActionBarElement extends HTMLElement {
|
38
|
+
constructor() {
|
39
|
+
super(...arguments);
|
40
|
+
_ActionBarElement_instances.add(this);
|
41
|
+
_ActionBarElement_initialBarWidth.set(this, void 0);
|
42
|
+
_ActionBarElement_previousBarWidth.set(this, void 0);
|
43
|
+
_ActionBarElement_focusZoneAbortController.set(this, null);
|
44
|
+
}
|
45
|
+
connectedCallback() {
|
46
|
+
var _a, _b, _c, _d;
|
47
|
+
__classPrivateFieldSet(this, _ActionBarElement_previousBarWidth, (_a = this.offsetWidth) !== null && _a !== void 0 ? _a : Infinity, "f");
|
48
|
+
__classPrivateFieldSet(this, _ActionBarElement_initialBarWidth, (_b = this.itemContainer.offsetWidth) !== null && _b !== void 0 ? _b : Infinity, "f");
|
49
|
+
// Calculate the width of all the items before hiding anything
|
50
|
+
for (const item of this.items) {
|
51
|
+
const width = item.getBoundingClientRect().width;
|
52
|
+
const marginLeft = parseInt((_c = window.getComputedStyle(item)) === null || _c === void 0 ? void 0 : _c.marginLeft, 10);
|
53
|
+
const marginRight = parseInt((_d = window.getComputedStyle(item)) === null || _d === void 0 ? void 0 : _d.marginRight, 10);
|
54
|
+
item.setAttribute('data-offset-width', `${width + marginLeft + marginRight}`);
|
55
|
+
}
|
56
|
+
resizeObserver.observe(this);
|
57
|
+
instersectionObserver.observe(this);
|
58
|
+
setTimeout(() => {
|
59
|
+
this.style.overflow = 'visible';
|
60
|
+
this.update();
|
61
|
+
}, 20); // Wait for the items to be rendered, making this really short to avoid a flash of unstyled content
|
62
|
+
}
|
63
|
+
disconnectedCallback() {
|
64
|
+
resizeObserver.unobserve(this);
|
65
|
+
instersectionObserver.unobserve(this);
|
66
|
+
}
|
67
|
+
menuItemClick(event) {
|
68
|
+
var _a;
|
69
|
+
const currentTarget = event.currentTarget;
|
70
|
+
const id = currentTarget === null || currentTarget === void 0 ? void 0 : currentTarget.getAttribute('data-for');
|
71
|
+
if (id) {
|
72
|
+
(_a = document.getElementById(id)) === null || _a === void 0 ? void 0 : _a.click();
|
73
|
+
}
|
74
|
+
}
|
75
|
+
update(rect = this.getBoundingClientRect()) {
|
76
|
+
// Only recalculate if the bar width changed
|
77
|
+
if (rect.width <= __classPrivateFieldGet(this, _ActionBarElement_previousBarWidth, "f") || __classPrivateFieldGet(this, _ActionBarElement_previousBarWidth, "f") === 0) {
|
78
|
+
__classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_shrink).call(this);
|
79
|
+
}
|
80
|
+
else if (rect.width > __classPrivateFieldGet(this, _ActionBarElement_previousBarWidth, "f")) {
|
81
|
+
__classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_grow).call(this);
|
82
|
+
}
|
83
|
+
__classPrivateFieldSet(this, _ActionBarElement_previousBarWidth, rect.width, "f");
|
84
|
+
if (rect.width <= __classPrivateFieldGet(this, _ActionBarElement_initialBarWidth, "f")) {
|
85
|
+
this.style.justifyContent = 'space-between';
|
86
|
+
}
|
87
|
+
else {
|
88
|
+
this.style.justifyContent = 'flex-end';
|
89
|
+
}
|
90
|
+
if (__classPrivateFieldGet(this, _ActionBarElement_focusZoneAbortController, "f")) {
|
91
|
+
__classPrivateFieldGet(this, _ActionBarElement_focusZoneAbortController, "f").abort();
|
92
|
+
}
|
93
|
+
__classPrivateFieldSet(this, _ActionBarElement_focusZoneAbortController, focusZone(this.itemContainer, {
|
94
|
+
bindKeys: FocusKeys.ArrowHorizontal | FocusKeys.HomeAndEnd,
|
95
|
+
focusOutBehavior: 'wrap',
|
96
|
+
focusableElementFilter: element => {
|
97
|
+
return !element.closest('.ActionBar-item[hidden]');
|
98
|
+
}
|
99
|
+
}), "f");
|
100
|
+
}
|
101
|
+
};
|
102
|
+
_ActionBarElement_initialBarWidth = new WeakMap(), _ActionBarElement_previousBarWidth = new WeakMap(), _ActionBarElement_focusZoneAbortController = new WeakMap(), _ActionBarElement_instances = new WeakSet(), _ActionBarElement_itemGap = function _ActionBarElement_itemGap() {
|
103
|
+
var _a;
|
104
|
+
return parseInt((_a = window.getComputedStyle(this.itemContainer)) === null || _a === void 0 ? void 0 : _a.columnGap, 10) || 0;
|
105
|
+
}, _ActionBarElement_availableSpace = function _ActionBarElement_availableSpace() {
|
106
|
+
// Get the offset of the item container from the container edge
|
107
|
+
return this.offsetWidth - this.itemContainer.offsetWidth;
|
108
|
+
}, _ActionBarElement_menuSpace = function _ActionBarElement_menuSpace() {
|
109
|
+
if (this.moreMenu.hidden) {
|
110
|
+
return 0;
|
111
|
+
}
|
112
|
+
return this.moreMenu.offsetWidth + __classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_itemGap).call(this);
|
113
|
+
}, _ActionBarElement_shrink = function _ActionBarElement_shrink() {
|
114
|
+
if (this.items[0].hidden) {
|
115
|
+
return;
|
116
|
+
}
|
117
|
+
let index = this.items.length - 1;
|
118
|
+
for (const item of this.items.reverse()) {
|
119
|
+
if (!item.hidden && __classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_availableSpace).call(this) < __classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_menuSpace).call(this)) {
|
120
|
+
__classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_hideItem).call(this, index);
|
121
|
+
}
|
122
|
+
else if (__classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_availableSpace).call(this) >= __classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_menuSpace).call(this)) {
|
123
|
+
return;
|
124
|
+
}
|
125
|
+
index--;
|
126
|
+
}
|
127
|
+
}, _ActionBarElement_grow = function _ActionBarElement_grow() {
|
128
|
+
// If last item is visible, there is no need to grow
|
129
|
+
if (!this.items[this.items.length - 1].hidden) {
|
130
|
+
return;
|
131
|
+
}
|
132
|
+
let index = 0;
|
133
|
+
for (const item of this.items) {
|
134
|
+
if (item.hidden) {
|
135
|
+
const offsetWidth = Number(item.getAttribute('data-offset-width'));
|
136
|
+
if (__classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_availableSpace).call(this) >= __classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_menuSpace).call(this) + offsetWidth || index === this.items.length - 1) {
|
137
|
+
__classPrivateFieldGet(this, _ActionBarElement_instances, "m", _ActionBarElement_showItem).call(this, index);
|
138
|
+
}
|
139
|
+
else {
|
140
|
+
return;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
index++;
|
144
|
+
}
|
145
|
+
if (!this.items[this.items.length - 1].hidden) {
|
146
|
+
this.moreMenu.hidden = true;
|
147
|
+
}
|
148
|
+
}, _ActionBarElement_showItem = function _ActionBarElement_showItem(index) {
|
149
|
+
this.items[index].hidden = false;
|
150
|
+
__classPrivateFieldGet(this, _ActionBarElement_instances, "a", _ActionBarElement_menuItems_get)[index].hidden = true;
|
151
|
+
}, _ActionBarElement_hideItem = function _ActionBarElement_hideItem(index) {
|
152
|
+
this.items[index].hidden = true;
|
153
|
+
__classPrivateFieldGet(this, _ActionBarElement_instances, "a", _ActionBarElement_menuItems_get)[index].hidden = false;
|
154
|
+
if (this.moreMenu.hidden) {
|
155
|
+
this.moreMenu.hidden = false;
|
156
|
+
}
|
157
|
+
}, _ActionBarElement_menuItems_get = function _ActionBarElement_menuItems_get() {
|
158
|
+
return this.moreMenu.querySelectorAll('[role="menu"] > li');
|
159
|
+
};
|
160
|
+
__decorate([
|
161
|
+
targets
|
162
|
+
], ActionBarElement.prototype, "items", void 0);
|
163
|
+
__decorate([
|
164
|
+
target
|
165
|
+
], ActionBarElement.prototype, "itemContainer", void 0);
|
166
|
+
__decorate([
|
167
|
+
target
|
168
|
+
], ActionBarElement.prototype, "moreMenu", void 0);
|
169
|
+
ActionBarElement = __decorate([
|
170
|
+
controller
|
171
|
+
], ActionBarElement);
|
172
|
+
window.ActionBarElement = ActionBarElement;
|
@@ -0,0 +1,175 @@
|
|
1
|
+
import {controller, targets, target} from '@github/catalyst'
|
2
|
+
import {focusZone, FocusKeys} from '@primer/behaviors'
|
3
|
+
|
4
|
+
const instersectionObserver = new IntersectionObserver(entries => {
|
5
|
+
for (const entry of entries) {
|
6
|
+
const action = entry.target
|
7
|
+
if (entry.isIntersecting && action instanceof ActionBarElement) {
|
8
|
+
action.update()
|
9
|
+
}
|
10
|
+
}
|
11
|
+
})
|
12
|
+
|
13
|
+
const resizeObserver = new ResizeObserver(entries => {
|
14
|
+
for (const entry of entries) {
|
15
|
+
const action = entry.target
|
16
|
+
if (action instanceof ActionBarElement) {
|
17
|
+
action.update(entry.contentRect)
|
18
|
+
}
|
19
|
+
}
|
20
|
+
})
|
21
|
+
|
22
|
+
@controller
|
23
|
+
class ActionBarElement extends HTMLElement {
|
24
|
+
@targets items: HTMLElement[]
|
25
|
+
@target itemContainer: HTMLElement
|
26
|
+
@target moreMenu: HTMLElement
|
27
|
+
|
28
|
+
#initialBarWidth: number
|
29
|
+
#previousBarWidth: number
|
30
|
+
#focusZoneAbortController: AbortController | null = null
|
31
|
+
|
32
|
+
connectedCallback() {
|
33
|
+
this.#previousBarWidth = this.offsetWidth ?? Infinity
|
34
|
+
this.#initialBarWidth = this.itemContainer.offsetWidth ?? Infinity
|
35
|
+
|
36
|
+
// Calculate the width of all the items before hiding anything
|
37
|
+
for (const item of this.items) {
|
38
|
+
const width = item.getBoundingClientRect().width
|
39
|
+
const marginLeft = parseInt(window.getComputedStyle(item)?.marginLeft, 10)
|
40
|
+
const marginRight = parseInt(window.getComputedStyle(item)?.marginRight, 10)
|
41
|
+
item.setAttribute('data-offset-width', `${width + marginLeft + marginRight}`)
|
42
|
+
}
|
43
|
+
|
44
|
+
resizeObserver.observe(this)
|
45
|
+
instersectionObserver.observe(this)
|
46
|
+
|
47
|
+
setTimeout(() => {
|
48
|
+
this.style.overflow = 'visible'
|
49
|
+
this.update()
|
50
|
+
}, 20) // Wait for the items to be rendered, making this really short to avoid a flash of unstyled content
|
51
|
+
}
|
52
|
+
|
53
|
+
disconnectedCallback() {
|
54
|
+
resizeObserver.unobserve(this)
|
55
|
+
instersectionObserver.unobserve(this)
|
56
|
+
}
|
57
|
+
|
58
|
+
menuItemClick(event: Event) {
|
59
|
+
const currentTarget = event.currentTarget as HTMLButtonElement
|
60
|
+
|
61
|
+
const id = currentTarget?.getAttribute('data-for')
|
62
|
+
|
63
|
+
if (id) {
|
64
|
+
document.getElementById(id)?.click()
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
update(rect: DOMRect = this.getBoundingClientRect()) {
|
69
|
+
// Only recalculate if the bar width changed
|
70
|
+
if (rect.width <= this.#previousBarWidth || this.#previousBarWidth === 0) {
|
71
|
+
this.#shrink()
|
72
|
+
} else if (rect.width > this.#previousBarWidth) {
|
73
|
+
this.#grow()
|
74
|
+
}
|
75
|
+
this.#previousBarWidth = rect.width
|
76
|
+
|
77
|
+
if (rect.width <= this.#initialBarWidth) {
|
78
|
+
this.style.justifyContent = 'space-between'
|
79
|
+
} else {
|
80
|
+
this.style.justifyContent = 'flex-end'
|
81
|
+
}
|
82
|
+
if (this.#focusZoneAbortController) {
|
83
|
+
this.#focusZoneAbortController.abort()
|
84
|
+
}
|
85
|
+
this.#focusZoneAbortController = focusZone(this.itemContainer, {
|
86
|
+
bindKeys: FocusKeys.ArrowHorizontal | FocusKeys.HomeAndEnd,
|
87
|
+
focusOutBehavior: 'wrap',
|
88
|
+
focusableElementFilter: element => {
|
89
|
+
return !element.closest('.ActionBar-item[hidden]')
|
90
|
+
}
|
91
|
+
})
|
92
|
+
}
|
93
|
+
|
94
|
+
#itemGap(): number {
|
95
|
+
return parseInt(window.getComputedStyle(this.itemContainer)?.columnGap, 10) || 0
|
96
|
+
}
|
97
|
+
|
98
|
+
#availableSpace(): number {
|
99
|
+
// Get the offset of the item container from the container edge
|
100
|
+
return this.offsetWidth - this.itemContainer.offsetWidth
|
101
|
+
}
|
102
|
+
|
103
|
+
#menuSpace(): number {
|
104
|
+
if (this.moreMenu.hidden) {
|
105
|
+
return 0
|
106
|
+
}
|
107
|
+
return this.moreMenu.offsetWidth + this.#itemGap()
|
108
|
+
}
|
109
|
+
|
110
|
+
#shrink() {
|
111
|
+
if (this.items[0]!.hidden) {
|
112
|
+
return
|
113
|
+
}
|
114
|
+
|
115
|
+
let index = this.items.length - 1
|
116
|
+
for (const item of this.items.reverse()) {
|
117
|
+
if (!item.hidden && this.#availableSpace() < this.#menuSpace()) {
|
118
|
+
this.#hideItem(index)
|
119
|
+
} else if (this.#availableSpace() >= this.#menuSpace()) {
|
120
|
+
return
|
121
|
+
}
|
122
|
+
index--
|
123
|
+
}
|
124
|
+
}
|
125
|
+
|
126
|
+
#grow() {
|
127
|
+
// If last item is visible, there is no need to grow
|
128
|
+
if (!this.items[this.items.length - 1]!.hidden) {
|
129
|
+
return
|
130
|
+
}
|
131
|
+
let index = 0
|
132
|
+
for (const item of this.items) {
|
133
|
+
if (item.hidden) {
|
134
|
+
const offsetWidth = Number(item.getAttribute('data-offset-width'))
|
135
|
+
|
136
|
+
if (this.#availableSpace() >= this.#menuSpace() + offsetWidth || index === this.items.length - 1) {
|
137
|
+
this.#showItem(index)
|
138
|
+
} else {
|
139
|
+
return
|
140
|
+
}
|
141
|
+
}
|
142
|
+
index++
|
143
|
+
}
|
144
|
+
|
145
|
+
if (!this.items[this.items.length - 1]!.hidden) {
|
146
|
+
this.moreMenu.hidden = true
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
#showItem(index: number) {
|
151
|
+
this.items[index]!.hidden = false
|
152
|
+
this.#menuItems[index]!.hidden = true
|
153
|
+
}
|
154
|
+
|
155
|
+
#hideItem(index: number) {
|
156
|
+
this.items[index]!.hidden = true
|
157
|
+
this.#menuItems[index]!.hidden = false
|
158
|
+
|
159
|
+
if (this.moreMenu.hidden) {
|
160
|
+
this.moreMenu.hidden = false
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
get #menuItems(): NodeListOf<HTMLElement> {
|
165
|
+
return this.moreMenu.querySelectorAll('[role="menu"] > li')
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
declare global {
|
170
|
+
interface Window {
|
171
|
+
ActionBarElement: typeof ActionBarElement
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
window.ActionBarElement = ActionBarElement
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import IncludeFragmentElement from '@github/include-fragment-element';
|
2
|
-
|
3
|
-
|
2
|
+
type SelectVariant = 'none' | 'single' | 'multiple' | null;
|
3
|
+
type SelectedItem = {
|
4
4
|
label: string | null | undefined;
|
5
5
|
value: string | null | undefined;
|
6
6
|
element: Element;
|
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
import '@oddbird/popover-polyfill';
|
2
|
+
type Direction = 'n' | 's' | 'e' | 'w' | 'ne' | 'se' | 'nw' | 'sw';
|
2
3
|
declare class ToolTipElement extends HTMLElement {
|
3
4
|
#private;
|
4
5
|
styles(): string;
|
@@ -13,7 +14,7 @@ declare class ToolTipElement extends HTMLElement {
|
|
13
14
|
get hiddenFromView(): true | false;
|
14
15
|
connectedCallback(): void;
|
15
16
|
disconnectedCallback(): void;
|
16
|
-
handleEvent(event: Event): void
|
17
|
+
handleEvent(event: Event): Promise<void>;
|
17
18
|
static observedAttributes: string[];
|
18
19
|
attributeChangedCallback(name: string): void;
|
19
20
|
}
|