openproject-primer_view_components 0.64.1 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view.d.ts +29 -0
- data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_icon_pair_element.d.ts +15 -0
- data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_include_fragment_element.d.ts +9 -0
- data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_roving_tab_index.d.ts +3 -0
- data/app/assets/javascripts/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.d.ts +38 -0
- data/app/assets/javascripts/components/primer/primer.d.ts +4 -0
- data/app/assets/javascripts/components/primer/shared_events.d.ts +15 -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/select_panel.css +1 -1
- data/app/components/primer/alpha/select_panel.css.json +2 -2
- data/app/components/primer/alpha/select_panel.css.map +1 -1
- data/app/components/primer/alpha/select_panel.html.erb +1 -1
- data/app/components/primer/alpha/select_panel.pcss +5 -2
- data/app/components/primer/beta/spinner.html.erb +1 -1
- data/app/components/primer/beta/spinner.rb +2 -0
- data/app/components/primer/open_project/file_tree_view/directory_node.html.erb +5 -0
- data/app/components/primer/open_project/file_tree_view/directory_node.rb +24 -0
- data/app/components/primer/open_project/file_tree_view/file_node.html.erb +2 -0
- data/app/components/primer/open_project/file_tree_view/file_node.rb +14 -0
- data/app/components/primer/open_project/file_tree_view.rb +15 -0
- data/app/components/primer/open_project/skeleton_box.css +1 -0
- data/app/components/primer/open_project/skeleton_box.css.json +6 -0
- data/app/components/primer/open_project/skeleton_box.css.map +1 -0
- data/app/components/primer/open_project/skeleton_box.html.erb +1 -0
- data/app/components/primer/open_project/skeleton_box.pcss +30 -0
- data/app/components/primer/open_project/skeleton_box.rb +27 -0
- data/app/components/primer/open_project/tree_view/icon.html.erb +1 -0
- data/app/components/primer/open_project/tree_view/icon.rb +22 -0
- data/app/components/primer/open_project/tree_view/icon_pair.html.erb +13 -0
- data/app/components/primer/open_project/tree_view/icon_pair.rb +42 -0
- data/app/components/primer/open_project/tree_view/leading_action.html.erb +3 -0
- data/app/components/primer/open_project/tree_view/leading_action.rb +18 -0
- data/app/components/primer/open_project/tree_view/leaf_node.html.erb +18 -0
- data/app/components/primer/open_project/tree_view/leaf_node.rb +96 -0
- data/app/components/primer/open_project/tree_view/loading_failure_message.html.erb +13 -0
- data/app/components/primer/open_project/tree_view/loading_failure_message.rb +31 -0
- data/app/components/primer/open_project/tree_view/node.html.erb +32 -0
- data/app/components/primer/open_project/tree_view/node.rb +155 -0
- data/app/components/primer/open_project/tree_view/skeleton_loader.html.erb +23 -0
- data/app/components/primer/open_project/tree_view/skeleton_loader.rb +36 -0
- data/app/components/primer/open_project/tree_view/spinner_loader.html.erb +20 -0
- data/app/components/primer/open_project/tree_view/spinner_loader.rb +33 -0
- data/app/components/primer/open_project/tree_view/sub_tree.html.erb +21 -0
- data/app/components/primer/open_project/tree_view/sub_tree.rb +106 -0
- data/app/components/primer/open_project/tree_view/sub_tree_container.html.erb +3 -0
- data/app/components/primer/open_project/tree_view/sub_tree_container.rb +39 -0
- data/app/components/primer/open_project/tree_view/sub_tree_node.html.erb +49 -0
- data/app/components/primer/open_project/tree_view/sub_tree_node.rb +172 -0
- data/app/components/primer/open_project/tree_view/tree_view.d.ts +29 -0
- data/app/components/primer/open_project/tree_view/tree_view.js +238 -0
- data/app/components/primer/open_project/tree_view/tree_view.ts +257 -0
- data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.d.ts +15 -0
- data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.js +62 -0
- data/app/components/primer/open_project/tree_view/tree_view_icon_pair_element.ts +56 -0
- data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.d.ts +9 -0
- data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.js +29 -0
- data/app/components/primer/open_project/tree_view/tree_view_include_fragment_element.ts +29 -0
- data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.d.ts +3 -0
- data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.js +126 -0
- data/app/components/primer/open_project/tree_view/tree_view_roving_tab_index.ts +156 -0
- data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.d.ts +38 -0
- data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.js +362 -0
- data/app/components/primer/open_project/tree_view/tree_view_sub_tree_node_element.ts +402 -0
- data/app/components/primer/open_project/tree_view/visual.html.erb +14 -0
- data/app/components/primer/open_project/tree_view/visual.rb +27 -0
- data/app/components/primer/open_project/tree_view.css +1 -0
- data/app/components/primer/open_project/tree_view.css.json +42 -0
- data/app/components/primer/open_project/tree_view.css.map +1 -0
- data/app/components/primer/open_project/tree_view.html.erb +7 -0
- data/app/components/primer/open_project/tree_view.pcss +319 -0
- data/app/components/primer/open_project/tree_view.rb +367 -0
- data/app/components/primer/primer.d.ts +4 -0
- data/app/components/primer/primer.js +4 -0
- data/app/components/primer/primer.pcss +2 -0
- data/app/components/primer/primer.ts +4 -0
- data/app/components/primer/shared_events.d.ts +15 -0
- data/app/components/primer/shared_events.ts +19 -0
- data/app/lib/primer/forms/acts_as_component.rb +1 -12
- data/lib/primer/view_components/version.rb +2 -2
- data/previews/primer/open_project/file_tree_view_preview/default.html.erb +16 -0
- data/previews/primer/open_project/file_tree_view_preview/playground.html.erb +4 -0
- data/previews/primer/open_project/file_tree_view_preview.rb +69 -0
- data/previews/primer/open_project/skeleton_box_preview.rb +20 -0
- data/previews/primer/open_project/tree_view_preview/default.html.erb +24 -0
- data/previews/primer/open_project/tree_view_preview/empty.html.erb +10 -0
- data/previews/primer/open_project/tree_view_preview/leaf_node_playground.html.erb +15 -0
- data/previews/primer/open_project/tree_view_preview/loading_failure.html.erb +36 -0
- data/previews/primer/open_project/tree_view_preview/loading_skeleton.html.erb +12 -0
- data/previews/primer/open_project/tree_view_preview/loading_spinner.html.erb +12 -0
- data/previews/primer/open_project/tree_view_preview/playground.html.erb +4 -0
- data/previews/primer/open_project/tree_view_preview.rb +139 -0
- data/static/arguments.json +388 -0
- data/static/audited_at.json +17 -0
- data/static/classes.json +15 -0
- data/static/constants.json +83 -0
- data/static/info_arch.json +1367 -0
- data/static/previews.json +167 -0
- data/static/statuses.json +17 -0
- metadata +75 -2
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
class TreeView
|
6
|
+
# A generic `TreeView` node.
|
7
|
+
#
|
8
|
+
# This component is part of the <%= link_to_component(Primer::OpenProject::TreeView) %> component and should
|
9
|
+
# not be used directly.
|
10
|
+
class Node < Primer::Component
|
11
|
+
# Generic leading action slot
|
12
|
+
renders_one :leading_action
|
13
|
+
|
14
|
+
# Generic leading visual slot
|
15
|
+
renders_one :leading_visual
|
16
|
+
|
17
|
+
# Generic trailing visual slot
|
18
|
+
renders_one :trailing_visual
|
19
|
+
|
20
|
+
# Generic toggle button slot
|
21
|
+
renders_one :toggle
|
22
|
+
|
23
|
+
# Generic text content slot (for node's label)
|
24
|
+
renders_one :text_content
|
25
|
+
|
26
|
+
# Wether or not this node is the current node.
|
27
|
+
#
|
28
|
+
# @return [Boolean]
|
29
|
+
attr_reader :current
|
30
|
+
alias current? current
|
31
|
+
|
32
|
+
# This node's checked state.
|
33
|
+
#
|
34
|
+
# @return [String]
|
35
|
+
attr_reader :checked
|
36
|
+
|
37
|
+
# This node's select variant (i.e. check box variant).
|
38
|
+
#
|
39
|
+
# @return [Symbol]
|
40
|
+
attr_reader :select_variant
|
41
|
+
|
42
|
+
DEFAULT_SELECT_VARIANT = :none
|
43
|
+
SELECT_VARIANT_OPTIONS = [
|
44
|
+
:multiple,
|
45
|
+
DEFAULT_SELECT_VARIANT
|
46
|
+
].freeze
|
47
|
+
|
48
|
+
DEFAULT_CHECKED_STATE = false
|
49
|
+
CHECKED_STATES = [
|
50
|
+
DEFAULT_CHECKED_STATE,
|
51
|
+
true,
|
52
|
+
"mixed"
|
53
|
+
]
|
54
|
+
|
55
|
+
# @param path [Array<String>] The node's "path," i.e. this node's label and the labels of all its ancestors. This node should be reachable by traversing the tree following this path.
|
56
|
+
# @param current [Boolean] Whether or not this node is the current node. The current node is styled differently than regular nodes and is the first element that receives focus when tabbing to the `TreeView` component.
|
57
|
+
# @param select_variant [Symbol] Controls the type of checkbox that appears. <%= one_of(Primer::OpenProject::TreeView::Node::SELECT_VARIANT_OPTIONS) %>
|
58
|
+
# @param checked [Boolean | String] The checked state of the node's checkbox. <%= one_of(Primer::OpenProject::TreeView::Node::CHECKED_STATES) %>
|
59
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList) %>.
|
60
|
+
def initialize(
|
61
|
+
path:,
|
62
|
+
current: false,
|
63
|
+
select_variant: DEFAULT_SELECT_VARIANT,
|
64
|
+
checked: DEFAULT_CHECKED_STATE,
|
65
|
+
**system_arguments
|
66
|
+
)
|
67
|
+
@system_arguments = deny_tag_argument(**system_arguments)
|
68
|
+
|
69
|
+
@path = path
|
70
|
+
@current = current
|
71
|
+
@select_variant = fetch_or_fallback(SELECT_VARIANT_OPTIONS, select_variant, DEFAULT_SELECT_VARIANT)
|
72
|
+
@checked = fetch_or_fallback(CHECKED_STATES, checked, DEFAULT_CHECKED_STATE)
|
73
|
+
|
74
|
+
@system_arguments[:tag] = :li
|
75
|
+
@system_arguments[:role] = :treeitem
|
76
|
+
@system_arguments[:tabindex] = current? ? 0 : -1
|
77
|
+
@system_arguments[:classes] = class_names(
|
78
|
+
@system_arguments.delete(:classes),
|
79
|
+
"TreeViewItem"
|
80
|
+
)
|
81
|
+
|
82
|
+
@system_arguments[:aria] = merge_aria(
|
83
|
+
@system_arguments, {
|
84
|
+
aria: {
|
85
|
+
level: level,
|
86
|
+
selected: false,
|
87
|
+
checked: checked,
|
88
|
+
labelledby: content_id
|
89
|
+
}
|
90
|
+
}
|
91
|
+
)
|
92
|
+
|
93
|
+
@system_arguments[:data] = merge_data(
|
94
|
+
@system_arguments,
|
95
|
+
{ data: { path: @path.to_json } }
|
96
|
+
)
|
97
|
+
|
98
|
+
return unless current?
|
99
|
+
|
100
|
+
@system_arguments[:aria] = merge_aria(
|
101
|
+
@system_arguments,
|
102
|
+
{ aria: { current: true } }
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
# The numeric depth of this node.
|
107
|
+
#
|
108
|
+
# @return [Integer] This node's depth.
|
109
|
+
def level
|
110
|
+
@level ||= @path.size
|
111
|
+
end
|
112
|
+
|
113
|
+
# Merges the given arguments into the current hash of system arguments provided when the component was
|
114
|
+
# initially constructed. This method can be used to add additional arguments just before rendering.
|
115
|
+
#
|
116
|
+
# @param other_arguments [Hash] The other hash of system arguments to merge into the current one.
|
117
|
+
def merge_system_arguments!(**other_arguments)
|
118
|
+
@system_arguments[:aria] = merge_aria(
|
119
|
+
@system_arguments,
|
120
|
+
other_arguments
|
121
|
+
)
|
122
|
+
|
123
|
+
@system_arguments[:data] = merge_data(
|
124
|
+
@system_arguments,
|
125
|
+
other_arguments
|
126
|
+
)
|
127
|
+
|
128
|
+
@system_arguments.merge!(**other_arguments)
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def before_render
|
134
|
+
if leading_visual?
|
135
|
+
end
|
136
|
+
|
137
|
+
if leading_action?
|
138
|
+
@system_arguments[:data] = merge_data(
|
139
|
+
@system_arguments,
|
140
|
+
{ data: { "has-leading-action": true } }
|
141
|
+
)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def content_id
|
146
|
+
@content_id ||= "#{base_id}-content"
|
147
|
+
end
|
148
|
+
|
149
|
+
def base_id
|
150
|
+
@base_id ||= self.class.generate_id
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<% unless loading_failure_message? %>
|
2
|
+
<% with_loading_failure_message # set the default %>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<%= render(Primer::BaseComponent.new(tag: :"tree-view-include-fragment", src: @src, loading: :lazy, data: { target: "tree-view-sub-tree-node.subTree tree-view-sub-tree-node.includeFragment", path: @container.path.to_json }, hidden: @container.expanded?, accept: "text/fragment+html")) do %>
|
6
|
+
<%= render(@container) do %>
|
7
|
+
<%= render(Primer::OpenProject::TreeView::Node.new(path: [*@container.path, :loader])) do |node| %>
|
8
|
+
<% node.with_text_content do %>
|
9
|
+
<div data-target="tree-view-sub-tree-node.loadingIndicator">
|
10
|
+
<% @count.times do %>
|
11
|
+
<span class="TreeViewSkeletonItemContainerStyle TreeViewItemSkeleton">
|
12
|
+
<%= render(Primer::OpenProject::SkeletonBox.new(width: "16px")) %>
|
13
|
+
<%= render(Primer::OpenProject::SkeletonBox.new(width: "100%", classes: "TreeItemSkeletonTextStyles")) %>
|
14
|
+
</span>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
17
|
+
<div data-target="tree-view-sub-tree-node.loadingFailureMessage" hidden>
|
18
|
+
<%= loading_failure_message %>
|
19
|
+
</div>
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
class TreeView
|
6
|
+
# Renders a loading skeleton for a `TreeView` sub-tree node.
|
7
|
+
#
|
8
|
+
# This component is part of the <%= link_to_component(Primer::OpenProject::TreeView) %> component and should
|
9
|
+
# not be used directly.
|
10
|
+
class SkeletonLoader < Primer::Component
|
11
|
+
# The failure message that appears if loading nodes from the server fails.
|
12
|
+
#
|
13
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::OpenProject::TreeView::LoadingFailureMessage) %>.
|
14
|
+
renders_one :loading_failure_message, lambda { |**system_arguments|
|
15
|
+
system_arguments[:retry_button_arguments] ||= {}
|
16
|
+
system_arguments[:retry_button_arguments][:data] = merge_data(
|
17
|
+
system_arguments[:retry_button_arguments],
|
18
|
+
data: { target: "tree-view-sub-tree-node.retryButton" }
|
19
|
+
)
|
20
|
+
|
21
|
+
LoadingFailureMessage.new(**system_arguments)
|
22
|
+
}
|
23
|
+
|
24
|
+
# @param src [String] The URL to fetch nodes from.
|
25
|
+
# @param count [Integer] The number of skeleton nodes to render.
|
26
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::OpenProject::TreeView::SubTreeContainer) %>.
|
27
|
+
def initialize(src:, count: 3, **system_arguments)
|
28
|
+
@src = src
|
29
|
+
@count = count
|
30
|
+
|
31
|
+
@container = SubTreeContainer.new(**system_arguments, expanded: true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<% unless loading_failure_message? %>
|
2
|
+
<% with_loading_failure_message # set the default %>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<%= render(Primer::BaseComponent.new(tag: :"tree-view-include-fragment", src: @src, loading: :lazy, data: { target: "tree-view-sub-tree-node.subTree tree-view-sub-tree-node.includeFragment", path: @container.path.to_json }, hidden: @container.expanded?, accept: "text/fragment+html")) do %>
|
6
|
+
<%= render(@container) do %>
|
7
|
+
<%= render(Primer::OpenProject::TreeView::Node.new(path: [*@container.path, :loader], data: { target: "tree-view-sub-tree-node.loadingIndicator" })) do |node| %>
|
8
|
+
<% node.with_text_content { "Loading..." } %>
|
9
|
+
<% node.with_leading_visual do %>
|
10
|
+
<%= render(Primer::Beta::Spinner.new(size: :small, wrapper_arguments: { classes: "TreeViewItemVisual" })) %>
|
11
|
+
<% end %>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<%= render(Primer::OpenProject::TreeView::Node.new(path: [*@container.path, :failure_msg], data: { target: "tree-view-sub-tree-node.loadingFailureMessage" }, hidden: true)) do |node| %>
|
15
|
+
<% node.with_text_content do %>
|
16
|
+
<%= loading_failure_message %>
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
19
|
+
<% end %>
|
20
|
+
<% end %>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
class TreeView
|
6
|
+
# Renders a loading spinner for a `TreeView` sub-tree node.
|
7
|
+
#
|
8
|
+
# This component is part of the <%= link_to_component(Primer::OpenProject::TreeView) %> component and should
|
9
|
+
# not be used directly.
|
10
|
+
class SpinnerLoader < Primer::Component
|
11
|
+
# The failure message that appears if loading nodes from the server fails.
|
12
|
+
#
|
13
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::OpenProject::TreeView::LoadingFailureMessage) %>.
|
14
|
+
renders_one :loading_failure_message, lambda { |**system_arguments|
|
15
|
+
system_arguments[:retry_button_arguments] ||= {}
|
16
|
+
system_arguments[:retry_button_arguments][:data] = merge_data(
|
17
|
+
system_arguments[:retry_button_arguments],
|
18
|
+
data: { target: "tree-view-sub-tree-node.retryButton" }
|
19
|
+
)
|
20
|
+
|
21
|
+
LoadingFailureMessage.new(**system_arguments)
|
22
|
+
}
|
23
|
+
|
24
|
+
# @param src [String] The URL to fetch nodes from.
|
25
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::OpenProject::TreeView::SubTreeContainer) %>.
|
26
|
+
def initialize(src:, **system_arguments)
|
27
|
+
@src = src
|
28
|
+
@container = SubTreeContainer.new(**system_arguments, expanded: true)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<% if defer? %>
|
2
|
+
<%= loader %>
|
3
|
+
<% else %>
|
4
|
+
<% unless no_items_message? %>
|
5
|
+
<% with_no_items_message { "No items" } # set the default %>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<%= render(@container) do %>
|
9
|
+
<% if nodes.empty? %>
|
10
|
+
<%= render(Primer::OpenProject::TreeView::Node.new(path: path, data: { "no-items": true })) do |node| %>
|
11
|
+
<% node.with_text_content do %>
|
12
|
+
<%= no_items_message %>
|
13
|
+
<% end %>
|
14
|
+
<% end %>
|
15
|
+
<% else %>
|
16
|
+
<% nodes.each do |node| %>
|
17
|
+
<%= node %>
|
18
|
+
<% end %>
|
19
|
+
<% end %>
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
class TreeView
|
6
|
+
# A `TreeView` sub-tree.
|
7
|
+
#
|
8
|
+
# This component is part of the <%= link_to_component(Primer::OpenProject::TreeView) %> component and should
|
9
|
+
# not be used directly.
|
10
|
+
class SubTree < Primer::Component
|
11
|
+
# @!parse
|
12
|
+
# # Adds an leaf node to the tree. Leaf nodes are nodes that do not have children.
|
13
|
+
# #
|
14
|
+
# # @param component_klass [Class] The class to use instead of the default <%= link_to_component(Primer::OpenProject::TreeView::LeafNode) %>
|
15
|
+
# # @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Primer::OpenProject::TreeView::LeafNode) %>, or whatever class is passed as the `component_klass` argument.
|
16
|
+
# def with_leaf(**system_arguments, &block)
|
17
|
+
# end
|
18
|
+
|
19
|
+
# @!parse
|
20
|
+
# # Adds a sub-tree node to the tree. Sub-trees are nodes that have children, which can be both leaf nodes and other sub-trees.
|
21
|
+
# #
|
22
|
+
# # @param component_klass [Class] The class to use instead of the default <%= link_to_component(Primer::OpenProject::TreeView::SubTreeNode) %>
|
23
|
+
# # @param system_arguments [Hash] These arguments are forwarded to <%= link_to_component(Primer::OpenProject::TreeView::SubTreeNode) %>, or whatever class is passed as the `component_klass` argument.
|
24
|
+
# def with_sub_tree(**system_arguments, &block)
|
25
|
+
# end
|
26
|
+
|
27
|
+
renders_many :nodes, types: {
|
28
|
+
leaf: {
|
29
|
+
renders: lambda { |component_klass: LeafNode, label:, **system_arguments|
|
30
|
+
component_klass.new(
|
31
|
+
**system_arguments,
|
32
|
+
path: [*path, label],
|
33
|
+
label: label
|
34
|
+
)
|
35
|
+
},
|
36
|
+
|
37
|
+
as: :leaf
|
38
|
+
},
|
39
|
+
|
40
|
+
sub_tree: {
|
41
|
+
renders: lambda { |component_klass: SubTreeNode, label:, **system_arguments|
|
42
|
+
component_klass.new(
|
43
|
+
**system_arguments,
|
44
|
+
path: [*path, label],
|
45
|
+
label: label
|
46
|
+
)
|
47
|
+
},
|
48
|
+
|
49
|
+
as: :sub_tree
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
# @!parse
|
54
|
+
# # Adds a loader to this sub-tree that displays a spinner animation while nodes are fetched from the server.
|
55
|
+
# #
|
56
|
+
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::OpenProject::TreeView::SpinnerLoader) %>.
|
57
|
+
# def with_loading_spinner(**system_arguments, &block)
|
58
|
+
# end
|
59
|
+
|
60
|
+
# @!parse
|
61
|
+
# # Adds a loader to this sub-tree that displays a skeleton animation while nodes are fetched from the server.
|
62
|
+
# #
|
63
|
+
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::OpenProject::TreeView::SkeletonLoader) %>.
|
64
|
+
# def with_loading_spinner(**system_arguments, &block)
|
65
|
+
# end
|
66
|
+
|
67
|
+
renders_one :loader, types: {
|
68
|
+
spinner: {
|
69
|
+
renders: lambda { |**system_arguments|
|
70
|
+
SpinnerLoader.new(**system_arguments, path: path)
|
71
|
+
},
|
72
|
+
|
73
|
+
as: :loading_spinner
|
74
|
+
},
|
75
|
+
|
76
|
+
skeleton: {
|
77
|
+
renders: lambda { |**system_arguments|
|
78
|
+
SkeletonLoader.new(**system_arguments, path: path)
|
79
|
+
},
|
80
|
+
|
81
|
+
as: :loading_skeleton
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
# The message to display if this sub-tree contains no children.
|
86
|
+
renders_one :no_items_message
|
87
|
+
|
88
|
+
delegate :path, :expanded?, to: :@container
|
89
|
+
|
90
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::OpenProject::TreeView::SubTreeContainer) %>.
|
91
|
+
def initialize(**system_arguments)
|
92
|
+
system_arguments[:data] = merge_data(
|
93
|
+
system_arguments,
|
94
|
+
{ data: { target: "tree-view-sub-tree-node.subTree" } }
|
95
|
+
)
|
96
|
+
|
97
|
+
@container = SubTreeContainer.new(**system_arguments)
|
98
|
+
end
|
99
|
+
|
100
|
+
def defer?
|
101
|
+
loader?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
class TreeView
|
6
|
+
# This component is part of the <%= link_to_component(Primer::OpenProject::TreeView) %> component and should
|
7
|
+
# not be used directly.
|
8
|
+
class SubTreeContainer < Primer::Component
|
9
|
+
# The path to this node
|
10
|
+
#
|
11
|
+
# @return [Array<String>]
|
12
|
+
attr_reader :path
|
13
|
+
|
14
|
+
# Whether or not this sub-tree node renders expanded.
|
15
|
+
#
|
16
|
+
# @return [Boolean]
|
17
|
+
attr_reader :expanded
|
18
|
+
|
19
|
+
alias expanded? expanded
|
20
|
+
|
21
|
+
# @param path [Array<String>] The path to this node.
|
22
|
+
# @param expanded [Boolean] Whether or not this sub-tree node renders expanded.
|
23
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
24
|
+
def initialize(path:, expanded: false, **system_arguments)
|
25
|
+
@system_arguments = deny_tag_argument(**system_arguments)
|
26
|
+
@path = path
|
27
|
+
@expanded = expanded
|
28
|
+
|
29
|
+
@system_arguments[:tag] = :ul
|
30
|
+
@system_arguments[:role] = :group
|
31
|
+
@system_arguments[:p] = 0
|
32
|
+
@system_arguments[:m] = 0
|
33
|
+
@system_arguments[:style] = "list-style: none;"
|
34
|
+
@system_arguments[:hidden] = !expanded?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
<tree-view-sub-tree-node>
|
2
|
+
<%= render(@node) do |node| %>
|
3
|
+
<% if leading_action? %>
|
4
|
+
<% node.with_leading_action do %>
|
5
|
+
<%= leading_action %>
|
6
|
+
<% end %>
|
7
|
+
<% end %>
|
8
|
+
<% if leading_visual? %>
|
9
|
+
<% node.with_leading_visual do %>
|
10
|
+
<%= leading_visual %>
|
11
|
+
<% end %>
|
12
|
+
<% end %>
|
13
|
+
<% if trailing_visual? %>
|
14
|
+
<% node.with_trailing_visual do %>
|
15
|
+
<%= trailing_visual %>
|
16
|
+
<% end %>
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<% node.with_text_content { @label } %>
|
20
|
+
|
21
|
+
<% node.with_toggle do %>
|
22
|
+
<div
|
23
|
+
data-target="tree-view-sub-tree-node.toggleButton"
|
24
|
+
class="TreeViewItemToggle TreeViewItemToggleHover TreeViewItemToggleEnd">
|
25
|
+
<%= render(
|
26
|
+
Primer::Beta::Octicon.new(
|
27
|
+
icon: :"chevron-down",
|
28
|
+
size: :xsmall,
|
29
|
+
data: { target: "tree-view-sub-tree-node.expandedToggleIcon" },
|
30
|
+
hidden: !@sub_tree.expanded?,
|
31
|
+
style: "pointer-events: none;"
|
32
|
+
)
|
33
|
+
) %>
|
34
|
+
|
35
|
+
<%= render(
|
36
|
+
Primer::Beta::Octicon.new(
|
37
|
+
icon: :"chevron-right",
|
38
|
+
size: :xsmall,
|
39
|
+
data: { target: "tree-view-sub-tree-node.collapsedToggleIcon" },
|
40
|
+
hidden: @sub_tree.expanded?,
|
41
|
+
style: "pointer-events: none;"
|
42
|
+
)
|
43
|
+
) %>
|
44
|
+
</div>
|
45
|
+
<% end %>
|
46
|
+
|
47
|
+
<%= render(@sub_tree) %>
|
48
|
+
<% end %>
|
49
|
+
</tree-view-sub-tree-node>
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
class TreeView
|
6
|
+
# A `TreeView` sub-tree node.
|
7
|
+
#
|
8
|
+
# This component is part of the <%= link_to_component(Primer::OpenProject::TreeView) %> component and should
|
9
|
+
# not be used directly.
|
10
|
+
class SubTreeNode < Primer::Component
|
11
|
+
DEFAULT_SELECT_STRATEGY = :descendants
|
12
|
+
SELECT_STRATEGIES = [
|
13
|
+
:self,
|
14
|
+
DEFAULT_SELECT_STRATEGY
|
15
|
+
]
|
16
|
+
|
17
|
+
# @!parse
|
18
|
+
# # Adds a leading visual icon rendered to the left of the node's label.
|
19
|
+
# #
|
20
|
+
# # @param label [String] A label describing the visual, displayed only to screen readers.
|
21
|
+
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::OpenProject::TreeView::Icon) %>.
|
22
|
+
# def with_leading_visual_icon(label: nil, **system_arguments, &block)
|
23
|
+
# end
|
24
|
+
|
25
|
+
# @!parse
|
26
|
+
# # Adds a pair of leading visual icon rendered to the left of the node's label.
|
27
|
+
# #
|
28
|
+
# # @param label [String] A label describing the visual, displayed only to screen readers.
|
29
|
+
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::OpenProject::TreeView::IconPair) %>.
|
30
|
+
# def with_leading_visual_icons(label: nil, **system_arguments, &block)
|
31
|
+
# end
|
32
|
+
|
33
|
+
renders_one :leading_visual, types: {
|
34
|
+
icon: lambda { |label: nil, **system_arguments|
|
35
|
+
merge_system_arguments!(
|
36
|
+
aria: { describedby: leading_visual_label_id }
|
37
|
+
)
|
38
|
+
|
39
|
+
Visual.new(
|
40
|
+
id: leading_visual_label_id,
|
41
|
+
label: label,
|
42
|
+
visual: Icon.new(**system_arguments)
|
43
|
+
)
|
44
|
+
},
|
45
|
+
|
46
|
+
icons: lambda { |label: nil, **system_arguments|
|
47
|
+
merge_system_arguments!(
|
48
|
+
aria: { describedby: leading_visual_label_id }
|
49
|
+
)
|
50
|
+
|
51
|
+
system_arguments[:data] = merge_data(
|
52
|
+
system_arguments,
|
53
|
+
{ data: { target: "tree-view-sub-tree-node.iconPair" } }
|
54
|
+
)
|
55
|
+
|
56
|
+
Visual.new(
|
57
|
+
id: leading_visual_label_id,
|
58
|
+
label: label,
|
59
|
+
visual: IconPair.new(
|
60
|
+
**system_arguments,
|
61
|
+
expanded: @sub_tree.expanded?,
|
62
|
+
)
|
63
|
+
)
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
# @!parse
|
68
|
+
# # Adds a leading action rendered to the left of the node's label and any leading visuals or checkboxes.
|
69
|
+
# #
|
70
|
+
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Beta::IconButton) %>.
|
71
|
+
# def with_leading_action_button(**system_arguments, &block)
|
72
|
+
# end
|
73
|
+
|
74
|
+
renders_one :leading_action, types: {
|
75
|
+
button: lambda { |**system_arguments|
|
76
|
+
LeadingAction.new(
|
77
|
+
action: Primer::Beta::IconButton.new(
|
78
|
+
scheme: :invisible,
|
79
|
+
**system_arguments
|
80
|
+
)
|
81
|
+
)
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
# @!parse
|
86
|
+
# # Adds a trailing visual icon rendered to the right of the node's label.
|
87
|
+
# #
|
88
|
+
# # @param label [String] A label describing the visual, displayed only to screen readers.
|
89
|
+
# # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::OpenProject::TreeView::Icon) %>.
|
90
|
+
# def with_trailing_visual_icon(label: nil, **system_arguments, &block)
|
91
|
+
# end
|
92
|
+
|
93
|
+
renders_one :trailing_visual, types: {
|
94
|
+
icon: lambda { |**system_arguments|
|
95
|
+
label = system_arguments.delete(:label)
|
96
|
+
|
97
|
+
Visual.new(
|
98
|
+
id: nil,
|
99
|
+
visual: Icon.new(**system_arguments),
|
100
|
+
label: label
|
101
|
+
)
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
delegate :with_leaf, :with_sub_tree, :with_loading_spinner, :with_loading_skeleton, to: :@sub_tree
|
106
|
+
delegate :current?, :merge_system_arguments!, to: :@node
|
107
|
+
|
108
|
+
# @param label [String] The node's label, i.e. it's textual content.
|
109
|
+
# @param path [Array<String>] The node's "path," i.e. this node's label and the labels of all its ancestors. This node should be reachable by traversing the tree following this path.
|
110
|
+
# @param expanded [Boolean] Whether or not this sub-tree should be rendered expanded.
|
111
|
+
# @param select_strategy [Symbol] What should happen when this sub-tree node is checked. <%= one_of(Primer::OpenProject::TreeView::SubTreeNode::SELECT_STRATEGIES) %>
|
112
|
+
# @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::OpenProject::TreeView::Node) %>.
|
113
|
+
def initialize(label:, path:, expanded: false, select_strategy: DEFAULT_SELECT_STRATEGY, **system_arguments)
|
114
|
+
@label = label
|
115
|
+
@system_arguments = system_arguments
|
116
|
+
@select_strategy = fetch_or_fallback(SELECT_STRATEGIES, select_strategy, DEFAULT_SELECT_STRATEGY)
|
117
|
+
|
118
|
+
@system_arguments[:aria] = merge_aria(
|
119
|
+
@system_arguments,
|
120
|
+
{ aria: { expanded: expanded } }
|
121
|
+
)
|
122
|
+
|
123
|
+
@system_arguments[:data] = merge_data(
|
124
|
+
@system_arguments, {
|
125
|
+
data: {
|
126
|
+
target: "tree-view-sub-tree-node.node",
|
127
|
+
"node-type": "sub-tree"
|
128
|
+
}
|
129
|
+
}
|
130
|
+
)
|
131
|
+
|
132
|
+
sub_tree_arguments = @system_arguments.delete(:sub_tree_arguments) || {}
|
133
|
+
|
134
|
+
@sub_tree = SubTree.new(
|
135
|
+
expanded: expanded,
|
136
|
+
path: path,
|
137
|
+
**sub_tree_arguments
|
138
|
+
)
|
139
|
+
|
140
|
+
@node = Primer::OpenProject::TreeView::Node.new(**@system_arguments, path: @sub_tree.path)
|
141
|
+
|
142
|
+
return if @node.select_variant == :none
|
143
|
+
|
144
|
+
@node.merge_system_arguments!(
|
145
|
+
data: {
|
146
|
+
"select-strategy": @select_strategy
|
147
|
+
}
|
148
|
+
)
|
149
|
+
end
|
150
|
+
|
151
|
+
def render_in(*args, &block)
|
152
|
+
super.tap do
|
153
|
+
# check this _after_ rendering so @sub_tree's slots are defined
|
154
|
+
if @node.select_variant != :none && @sub_tree.defer?
|
155
|
+
raise ArgumentError, "TreeView does not currently support select variants for sub-trees loaded asynchronously."
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
def base_id
|
163
|
+
@base_id ||= self.class.generate_id
|
164
|
+
end
|
165
|
+
|
166
|
+
def leading_visual_label_id
|
167
|
+
@leading_visual_id ||= "#{base_id}-leading-visual-label"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|