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
@@ -33,3 +33,7 @@ import './open_project/danger_dialog_form_helper';
|
|
33
33
|
import './open_project/collapsible';
|
34
34
|
import './open_project/border_box/collapsible_header';
|
35
35
|
import './open_project/collapsible_section';
|
36
|
+
import './open_project/tree_view/tree_view';
|
37
|
+
import './open_project/tree_view/tree_view_icon_pair_element';
|
38
|
+
import './open_project/tree_view/tree_view_sub_tree_node_element';
|
39
|
+
import './open_project/tree_view/tree_view_include_fragment_element';
|
@@ -33,3 +33,7 @@ import './open_project/danger_dialog_form_helper'
|
|
33
33
|
import './open_project/collapsible'
|
34
34
|
import './open_project/border_box/collapsible_header'
|
35
35
|
import './open_project/collapsible_section'
|
36
|
+
import './open_project/tree_view/tree_view'
|
37
|
+
import './open_project/tree_view/tree_view_icon_pair_element'
|
38
|
+
import './open_project/tree_view/tree_view_sub_tree_node_element'
|
39
|
+
import './open_project/tree_view/tree_view_include_fragment_element'
|
@@ -3,9 +3,24 @@ export type ItemActivatedEvent = {
|
|
3
3
|
checked: boolean;
|
4
4
|
value: string | null;
|
5
5
|
};
|
6
|
+
export type TreeViewNodeType = 'leaf' | 'sub-tree';
|
7
|
+
export type TreeViewCheckedValue = 'true' | 'false' | 'mixed';
|
8
|
+
export type TreeViewNodeInfo = {
|
9
|
+
node: Element;
|
10
|
+
type: TreeViewNodeType;
|
11
|
+
path: string[];
|
12
|
+
checkedValue: TreeViewCheckedValue;
|
13
|
+
previousCheckedValue: TreeViewCheckedValue;
|
14
|
+
};
|
6
15
|
declare global {
|
7
16
|
interface HTMLElementEventMap {
|
8
17
|
itemActivated: CustomEvent<ItemActivatedEvent>;
|
9
18
|
beforeItemActivated: CustomEvent<ItemActivatedEvent>;
|
19
|
+
treeViewNodeActivated: CustomEvent<TreeViewNodeInfo>;
|
20
|
+
treeViewBeforeNodeActivated: CustomEvent<TreeViewNodeInfo>;
|
21
|
+
treeViewNodeExpanded: CustomEvent<TreeViewNodeInfo>;
|
22
|
+
treeViewNodeCollapsed: CustomEvent<TreeViewNodeInfo>;
|
23
|
+
treeViewNodeChecked: CustomEvent<TreeViewNodeInfo[]>;
|
24
|
+
treeViewBeforeNodeChecked: CustomEvent<TreeViewNodeInfo[]>;
|
10
25
|
}
|
11
26
|
}
|
@@ -4,9 +4,28 @@ export type ItemActivatedEvent = {
|
|
4
4
|
value: string | null
|
5
5
|
}
|
6
6
|
|
7
|
+
export type TreeViewNodeType = 'leaf' | 'sub-tree'
|
8
|
+
export type TreeViewCheckedValue = 'true' | 'false' | 'mixed'
|
9
|
+
|
10
|
+
export type TreeViewNodeInfo = {
|
11
|
+
node: Element
|
12
|
+
type: TreeViewNodeType
|
13
|
+
path: string[]
|
14
|
+
checkedValue: TreeViewCheckedValue
|
15
|
+
previousCheckedValue: TreeViewCheckedValue
|
16
|
+
}
|
17
|
+
|
7
18
|
declare global {
|
8
19
|
interface HTMLElementEventMap {
|
9
20
|
itemActivated: CustomEvent<ItemActivatedEvent>
|
10
21
|
beforeItemActivated: CustomEvent<ItemActivatedEvent>
|
22
|
+
|
23
|
+
treeViewNodeActivated: CustomEvent<TreeViewNodeInfo>
|
24
|
+
treeViewBeforeNodeActivated: CustomEvent<TreeViewNodeInfo>
|
25
|
+
treeViewNodeExpanded: CustomEvent<TreeViewNodeInfo>
|
26
|
+
treeViewNodeCollapsed: CustomEvent<TreeViewNodeInfo>
|
27
|
+
|
28
|
+
treeViewNodeChecked: CustomEvent<TreeViewNodeInfo[]>
|
29
|
+
treeViewBeforeNodeChecked: CustomEvent<TreeViewNodeInfo[]>
|
11
30
|
}
|
12
31
|
}
|
@@ -8,7 +8,7 @@ module Primer
|
|
8
8
|
module ActsAsComponent
|
9
9
|
# :nodoc:
|
10
10
|
module InstanceMethods
|
11
|
-
delegate :render, :content_tag, :output_buffer, to: :@view_context
|
11
|
+
delegate :render, :content_tag, :output_buffer, :capture, to: :@view_context
|
12
12
|
|
13
13
|
def render_in(view_context, &block)
|
14
14
|
@view_context = view_context
|
@@ -16,17 +16,6 @@ module Primer
|
|
16
16
|
perform_render(&block)
|
17
17
|
end
|
18
18
|
|
19
|
-
# This is necessary to restore the functionality changed by https://github.com/rails/rails/pull/47194.
|
20
|
-
# I would love to remove this at some point, perhaps if we ever decide to replace
|
21
|
-
# ActsAsComponent with view component.
|
22
|
-
def capture(*args, &block)
|
23
|
-
old_buffer = @view_context.output_buffer
|
24
|
-
@view_context.output_buffer = ActionView::OutputBuffer.new
|
25
|
-
@view_context.capture(*args, &block)
|
26
|
-
ensure
|
27
|
-
@view_context.output_buffer = old_buffer
|
28
|
-
end
|
29
|
-
|
30
19
|
# :nocov:
|
31
20
|
def perform_render(&_block)
|
32
21
|
raise NotImplementedError, "subclasses must implement ##{__method__}."
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<div style="max-width: 400px">
|
2
|
+
<%= render(Primer::OpenProject::FileTreeView.new) do |tree_view| %>
|
3
|
+
<% tree_view.with_directory(label: "src", expanded: expanded, select_variant: select_variant) do |dir| %>
|
4
|
+
<% dir.with_trailing_visual_icon(icon: :"diff-modified") %>
|
5
|
+
|
6
|
+
<% dir.with_file(label: "button.rb", select_variant: select_variant) %>
|
7
|
+
<% dir.with_file(label: "icon_button.rb", current: true, select_variant: select_variant) %>
|
8
|
+
|
9
|
+
<% dir.with_directory(label: "tree_view", select_variant: select_variant) do |subdir| %>
|
10
|
+
<% subdir.with_file(label: "sub_tree.rb", select_variant: select_variant) %>
|
11
|
+
<% end %>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<% tree_view.with_file(label: "action_menu.rb", select_variant: select_variant) %>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
# @label FileTreeView
|
6
|
+
class FileTreeViewPreview < ViewComponent::Preview
|
7
|
+
# @label Default
|
8
|
+
#
|
9
|
+
# @snapshot interactive
|
10
|
+
# @param expanded [Boolean] toggle
|
11
|
+
# @param select_variant [Symbol] select [multiple, none]
|
12
|
+
def default(expanded: false, select_variant: :none)
|
13
|
+
render_with_template(locals: {
|
14
|
+
expanded: coerce_bool(expanded),
|
15
|
+
select_variant: select_variant.to_sym
|
16
|
+
})
|
17
|
+
end
|
18
|
+
|
19
|
+
# @label Playground
|
20
|
+
#
|
21
|
+
# @param expanded [Boolean] toggle
|
22
|
+
# @param select_variant [Symbol] select [multiple, none]
|
23
|
+
def playground(expanded: false, select_variant: :none)
|
24
|
+
render_with_template(locals: {
|
25
|
+
expanded: coerce_bool(expanded),
|
26
|
+
select_variant: select_variant.to_sym,
|
27
|
+
populate: -> (*args) { populate(*args) }
|
28
|
+
})
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def coerce_bool(value)
|
34
|
+
case value
|
35
|
+
when true, false
|
36
|
+
value
|
37
|
+
when "true"
|
38
|
+
true
|
39
|
+
when "false"
|
40
|
+
false
|
41
|
+
else
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def populate(node, data, node_arguments)
|
47
|
+
return unless data
|
48
|
+
|
49
|
+
entries = (
|
50
|
+
data.fetch("children", {}).keys.map { |label, idx| [label, :directory] } +
|
51
|
+
data.fetch("files", []).map { |label| [label, :file] }
|
52
|
+
)
|
53
|
+
|
54
|
+
entries.sort_by!(&:first)
|
55
|
+
|
56
|
+
entries.each do |label, kind, idx|
|
57
|
+
case kind
|
58
|
+
when :directory
|
59
|
+
node.with_directory(label: label, **node_arguments) do |sub_tree|
|
60
|
+
populate(sub_tree, data["children"][label], node_arguments)
|
61
|
+
end
|
62
|
+
when :file
|
63
|
+
node.with_file(label: label, **node_arguments)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
# @label SkeletonBox
|
6
|
+
class SkeletonBoxPreview < ViewComponent::Preview
|
7
|
+
# @label Default
|
8
|
+
def default
|
9
|
+
render(Primer::OpenProject::SkeletonBox.new(width: "64px", height: "64px"))
|
10
|
+
end
|
11
|
+
|
12
|
+
# @label Playground
|
13
|
+
# @param width text
|
14
|
+
# @param height text
|
15
|
+
def playground(width: "64px", height: "64px")
|
16
|
+
render(Primer::OpenProject::SkeletonBox.new(width: width, height: height))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<div style="max-width: 400px">
|
2
|
+
<%= render(Primer::OpenProject::TreeView.new) do |tree_view| %>
|
3
|
+
<% tree_view.with_sub_tree(label: "src", expanded: expanded, select_variant: select_variant, select_strategy: select_strategy) do |sub_tree| %>
|
4
|
+
<% sub_tree.with_leading_visual_icons(label: "Foobar") do |icons| %>
|
5
|
+
<% icons.with_expanded_icon(icon: :"file-directory-open-fill", color: :accent) %>
|
6
|
+
<% icons.with_collapsed_icon(icon: :"file-directory-fill", color: :accent) %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<% sub_tree.with_trailing_visual_icon(icon: :"diff-modified") %>
|
10
|
+
|
11
|
+
<% sub_tree.with_leaf(label: "button.rb", select_variant: select_variant) do |item| %>
|
12
|
+
<% item.with_leading_visual_icon(icon: :file) %>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<% sub_tree.with_leaf(label: "icon_button.rb", current: true, select_variant: select_variant) do |item| %>
|
16
|
+
<% item.with_leading_visual_icon(icon: :file) %>
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% tree_view.with_leaf(label: "action_menu.rb", select_variant: select_variant) do |item| %>
|
21
|
+
<% item.with_leading_visual_icon(icon: :file) %>
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<div style="max-width: 400px">
|
2
|
+
<%= render(Primer::OpenProject::TreeView.new) do |tree_view| %>
|
3
|
+
<% tree_view.with_sub_tree(label: "src") do |sub_tree| %>
|
4
|
+
<% sub_tree.with_leading_visual_icons do |icons| %>
|
5
|
+
<% icons.with_expanded_icon(icon: :"file-directory-open-fill", color: :accent) %>
|
6
|
+
<% icons.with_collapsed_icon(icon: :"file-directory-fill", color: :accent) %>
|
7
|
+
<% end %>
|
8
|
+
<% end %>
|
9
|
+
<% end %>
|
10
|
+
</div>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<%= render(Primer::OpenProject::TreeView.new) do |tree| %>
|
2
|
+
<% tree.with_leaf(label: label, select_variant: select_variant) do |node| %>
|
3
|
+
<% if leading_visual_icon && leading_visual_icon != :none %>
|
4
|
+
<% node.with_leading_visual_icon(icon: leading_visual_icon) %>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<% if trailing_visual_icon && trailing_visual_icon != :none %>
|
8
|
+
<% node.with_trailing_visual_icon(icon: trailing_visual_icon) %>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<% if leading_action_icon && leading_action_icon != :none %>
|
12
|
+
<% node.with_leading_action_button(icon: leading_action_icon, aria: { label: "Leading action icon" }) %>
|
13
|
+
<% end %>
|
14
|
+
<% end %>
|
15
|
+
<% end %>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<% subject_id = SecureRandom.hex %>
|
2
|
+
|
3
|
+
<div style="max-width: 400px">
|
4
|
+
<%= render(Primer::OpenProject::TreeView.new(data: { interaction_subject: subject_id })) do |tree_view| %>
|
5
|
+
<% tree_view.with_sub_tree(label: "primer") do |sub_tree| %>
|
6
|
+
<% sub_tree.with_leading_visual_icons do |icons| %>
|
7
|
+
<% icons.with_expanded_icon(icon: :"file-directory-open-fill", color: :accent) %>
|
8
|
+
<% icons.with_collapsed_icon(icon: :"file-directory-fill", color: :accent) %>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<% sub_tree.with_loading_spinner(src: tree_view_items_path(loader: "spinner", fail: true)) %>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<script>
|
17
|
+
function ready(fn) {
|
18
|
+
if (document.readyState !== 'loading') {
|
19
|
+
fn()
|
20
|
+
} else {
|
21
|
+
document.addEventListener('DOMContentLoaded', fn)
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
ready(() => {
|
26
|
+
const subject = document.querySelector("[data-interaction-subject='<%= subject_id %>']")
|
27
|
+
if (!subject) return
|
28
|
+
|
29
|
+
const includeFragment = subject.querySelector('tree-view-include-fragment')
|
30
|
+
if (!includeFragment) return
|
31
|
+
|
32
|
+
includeFragment.addEventListener('loadend', (event) => {
|
33
|
+
subject.setAttribute('data-ready', 'true')
|
34
|
+
})
|
35
|
+
})
|
36
|
+
</script>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div style="max-width: 400px">
|
2
|
+
<%= render(Primer::OpenProject::TreeView.new) do |tree_view| %>
|
3
|
+
<% tree_view.with_sub_tree(label: "primer") do |sub_tree| %>
|
4
|
+
<% sub_tree.with_leading_visual_icons do |icons| %>
|
5
|
+
<% icons.with_expanded_icon(icon: :"file-directory-open-fill", color: :accent) %>
|
6
|
+
<% icons.with_collapsed_icon(icon: :"file-directory-fill", color: :accent) %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<% sub_tree.with_loading_skeleton(src: tree_view_items_path(loader: "skeleton", fail: simulate_failure, empty: simulate_empty)) %>
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
12
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div style="max-width: 400px">
|
2
|
+
<%= render(Primer::OpenProject::TreeView.new) do |tree_view| %>
|
3
|
+
<% tree_view.with_sub_tree(label: "primer") do |sub_tree| %>
|
4
|
+
<% sub_tree.with_leading_visual_icons do |icons| %>
|
5
|
+
<% icons.with_expanded_icon(icon: :"file-directory-open-fill", color: :accent) %>
|
6
|
+
<% icons.with_collapsed_icon(icon: :"file-directory-fill", color: :accent) %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<% sub_tree.with_loading_spinner(src: tree_view_items_path(loader: "spinner", fail: simulate_failure, empty: simulate_empty)) %>
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
12
|
+
</div>
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module OpenProject
|
5
|
+
# @label TreeView
|
6
|
+
class TreeViewPreview < ViewComponent::Preview
|
7
|
+
# @label Default
|
8
|
+
#
|
9
|
+
# @snapshot interactive
|
10
|
+
# @param expanded [Boolean] toggle
|
11
|
+
# @param select_variant [Symbol] select [multiple, none]
|
12
|
+
# @param select_strategy [Symbol] select [self, descendants]
|
13
|
+
def default(expanded: false, select_variant: :none, select_strategy: :descendants)
|
14
|
+
render_with_template(locals: {
|
15
|
+
expanded: coerce_bool(expanded),
|
16
|
+
select_variant: select_variant.to_sym,
|
17
|
+
select_strategy: select_strategy.to_sym
|
18
|
+
})
|
19
|
+
end
|
20
|
+
|
21
|
+
# @label Playground
|
22
|
+
#
|
23
|
+
# @param expanded [Boolean] toggle
|
24
|
+
# @param select_variant [Symbol] select [multiple, none]
|
25
|
+
# @param select_strategy [Symbol] select [self, descendants]
|
26
|
+
def playground(expanded: false, select_variant: :none, select_strategy: :descendants)
|
27
|
+
render_with_template(locals: {
|
28
|
+
expanded: coerce_bool(expanded),
|
29
|
+
select_variant: select_variant.to_sym,
|
30
|
+
select_strategy: select_strategy.to_sym,
|
31
|
+
populate: -> (*args) { populate(*args) }
|
32
|
+
})
|
33
|
+
end
|
34
|
+
|
35
|
+
# @label Empty
|
36
|
+
#
|
37
|
+
# @snapshot interactive
|
38
|
+
def empty
|
39
|
+
end
|
40
|
+
|
41
|
+
# @label Loading failure
|
42
|
+
#
|
43
|
+
# @snapshot interactive
|
44
|
+
def loading_failure
|
45
|
+
end
|
46
|
+
|
47
|
+
# @label Loading spinner
|
48
|
+
#
|
49
|
+
# @snapshot interactive
|
50
|
+
# @param simulate_failure [Boolean] toggle
|
51
|
+
# @param simulate_empty [Boolean] toggle
|
52
|
+
def loading_spinner(simulate_failure: false, simulate_empty: false)
|
53
|
+
render_with_template(locals: {
|
54
|
+
simulate_failure: coerce_bool(simulate_failure),
|
55
|
+
simulate_empty: coerce_bool(simulate_empty),
|
56
|
+
})
|
57
|
+
end
|
58
|
+
|
59
|
+
# @label Loading skeleton
|
60
|
+
#
|
61
|
+
# @snapshot interactive
|
62
|
+
# @param simulate_failure [Boolean] toggle
|
63
|
+
# @param simulate_empty [Boolean] toggle
|
64
|
+
def loading_skeleton(simulate_failure: false, simulate_empty: false)
|
65
|
+
render_with_template(locals: {
|
66
|
+
simulate_failure: coerce_bool(simulate_failure),
|
67
|
+
simulate_empty: coerce_bool(simulate_empty)
|
68
|
+
})
|
69
|
+
end
|
70
|
+
|
71
|
+
# @label Leaf node playground
|
72
|
+
#
|
73
|
+
# @param label [String] text
|
74
|
+
# @param leading_visual_icon [Symbol] octicon
|
75
|
+
# @param leading_action_icon [Symbol] octicon
|
76
|
+
# @param trailing_visual_icon [Symbol] octicon
|
77
|
+
# @param select_variant [Symbol] select [multiple, none]
|
78
|
+
def leaf_node_playground(
|
79
|
+
label: "Leaf node",
|
80
|
+
leading_visual_icon: nil,
|
81
|
+
leading_action_icon: nil,
|
82
|
+
trailing_visual_icon: nil,
|
83
|
+
select_variant: :none
|
84
|
+
)
|
85
|
+
render_with_template(locals: {
|
86
|
+
label: label,
|
87
|
+
leading_visual_icon: leading_visual_icon,
|
88
|
+
leading_action_icon: leading_action_icon,
|
89
|
+
trailing_visual_icon: trailing_visual_icon,
|
90
|
+
select_variant: select_variant.to_sym
|
91
|
+
})
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def coerce_bool(value)
|
97
|
+
case value
|
98
|
+
when true, false
|
99
|
+
value
|
100
|
+
when "true"
|
101
|
+
true
|
102
|
+
when "false"
|
103
|
+
false
|
104
|
+
else
|
105
|
+
false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def populate(node, data, node_arguments)
|
110
|
+
return unless data
|
111
|
+
|
112
|
+
entries = (
|
113
|
+
data.fetch("children", {}).keys.map { |label, idx| [label, :directory] } +
|
114
|
+
data.fetch("files", []).map { |label| [label, :file] }
|
115
|
+
)
|
116
|
+
|
117
|
+
entries.sort_by!(&:first)
|
118
|
+
|
119
|
+
entries.each do |label, kind, idx|
|
120
|
+
case kind
|
121
|
+
when :directory
|
122
|
+
node.with_sub_tree(label: label, **node_arguments) do |sub_tree|
|
123
|
+
sub_tree.with_leading_visual_icons do |icons|
|
124
|
+
icons.with_expanded_icon(icon: :"file-directory-open-fill", color: :accent)
|
125
|
+
icons.with_collapsed_icon(icon: :"file-directory-fill", color: :accent)
|
126
|
+
end
|
127
|
+
|
128
|
+
populate(sub_tree, data["children"][label], node_arguments)
|
129
|
+
end
|
130
|
+
when :file
|
131
|
+
node.with_leaf(label: label, **node_arguments) do |leaf|
|
132
|
+
leaf.with_leading_visual_icon(icon: :file)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|