1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
# frozen_string_literal: true
module Sidebars
class Menu
extend ::Gitlab::Utils::Override
include ::Gitlab::Routing
include GitlabRoutingHelper
include Gitlab::Allowable
include ::Sidebars::Concerns::HasPill
include ::Sidebars::Concerns::HasIcon
include ::Sidebars::Concerns::PositionableList
include ::Sidebars::Concerns::Renderable
include ::Sidebars::Concerns::ContainerWithHtmlOptions
include ::Sidebars::Concerns::HasActiveRoutes
include ::Sidebars::Concerns::HasPartial
attr_reader :context
delegate :current_user, :container, to: :@context
def initialize(context)
@context = context
@items = []
configure_menu_items
end
def configure_menu_items
true
end
override :render?
def render?
has_renderable_items? || menu_with_partial?
end
override :link
def link
renderable_items.first&.link
end
# This method normalizes the information retrieved from the submenus and this menu
# Value from menus is something like: [{ path: 'foo', path: 'bar', controller: :foo }]
# This method filters the information and returns: { path: ['foo', 'bar'], controller: :foo }
def all_active_routes
@all_active_routes ||=
([active_routes] + renderable_items.map(&:active_routes)).flatten.each_with_object({}) do |pairs, hash|
pairs.each do |k, v|
hash[k] ||= []
hash[k] += Array(v)
hash[k].uniq!
end
hash
end
end
# Returns whether the menu has any menu item, no
# matter whether it is renderable or not
def has_items?
@items.any?
end
# Returns all renderable menu items
def renderable_items
@renderable_items ||= @items.select(&:render?)
end
# Defines whether menu is separated from others with a top separator
def separated?
false
end
# Returns a tree-like representation of itself and all
# renderable menu entries, with additional information
# on whether the item(s) have an active route
def serialize_for_super_sidebar
items = serialize_items_for_super_sidebar
is_active = @context.route_is_active.call(active_routes) || items.any? { |item| item[:is_active] }
{
title: title,
icon: sprite_icon,
link: link,
is_active: is_active,
pill_count: has_pill? ? pill_count : nil,
items: items,
separated: separated?
}
end
# Returns an array of renderable menu entries,
# with additional information on whether the item
# has an active route
def serialize_items_for_super_sidebar
# All renderable menu entries
renderable_items.map do |entry|
entry.serialize_for_super_sidebar.tap do |item|
active_routes = item.delete(:active_routes)
item[:is_active] = active_routes ? @context.route_is_active.call(active_routes) : false
end
end
end
def pick_into_super_sidebar?
false
end
# Returns whether the menu has any renderable menu item
def has_renderable_items?
renderable_items.any?
end
def add_item(item)
add_element(@items, item)
end
def insert_item_before(before_item, new_item)
insert_element_before(@items, before_item, new_item)
end
def insert_item_after(after_item, new_item)
insert_element_after(@items, after_item, new_item)
end
def replace_placeholder(item)
idx = @items.index { |e| e.item_id == item.item_id && e.is_a?(::Sidebars::NilMenuItem) }
if idx.nil?
add_item(item)
else
replace_element(@items, item.item_id, item)
end
end
override :container_html_options
def container_html_options
super.tap do |html_options|
# Flagging menus that can be rendered and with renderable menu items
if render? && has_renderable_items?
html_options[:class] = [*html_options[:class], 'has-sub-items'].join(' ')
end
end
end
# Sometimes we want to convert a top-level Menu (e.g. Wiki/Snippets)
# to a MenuItem. This serializer is used in order to enable that conversion
def serialize_as_menu_item_args
{
title: title,
link: link,
active_routes: active_routes,
container_html_options: container_html_options
}
end
private
override :index_of
def index_of(list, element)
list.index { |e| e.item_id == element }
end
end
end
|