phlexi-menu 0.1.0 → 0.3.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/Appraisals +1 -1
- data/README.md +151 -126
- data/changes.patch +393 -0
- data/gemfiles/default.gemfile.lock +1 -1
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/lib/phlexi/menu/badge.rb +32 -0
- data/lib/phlexi/menu/builder.rb +0 -21
- data/lib/phlexi/menu/component.rb +87 -54
- data/lib/phlexi/menu/item.rb +79 -32
- data/lib/phlexi/menu/theme.rb +2 -0
- data/lib/phlexi/menu/version.rb +1 -1
- metadata +10 -10
- data/export.json +0 -82
- data/export.rb +0 -48
@@ -23,6 +23,8 @@ module Phlexi
|
|
23
23
|
# Theme class for customizing menu appearance
|
24
24
|
class Theme < Phlexi::Menu::Theme; end
|
25
25
|
|
26
|
+
class Badge < Phlexi::Menu::Badge; end
|
27
|
+
|
26
28
|
# @return [Integer] The default maximum nesting depth for menu items
|
27
29
|
DEFAULT_MAX_DEPTH = 3
|
28
30
|
|
@@ -31,7 +33,10 @@ module Phlexi
|
|
31
33
|
# @param menu [Phlexi::Menu::Builder] The menu structure to render
|
32
34
|
# @param max_depth [Integer] Maximum nesting depth for menu items
|
33
35
|
# @param options [Hash] Additional options passed to rendering methods
|
36
|
+
# @raise [ArgumentError] If menu is nil
|
34
37
|
def initialize(menu, max_depth: default_max_depth, **options)
|
38
|
+
raise ArgumentError, "Menu cannot be nil" if menu.nil?
|
39
|
+
|
35
40
|
@menu = menu
|
36
41
|
@max_depth = max_depth
|
37
42
|
@options = options
|
@@ -39,9 +44,7 @@ module Phlexi
|
|
39
44
|
end
|
40
45
|
|
41
46
|
def view_template
|
42
|
-
nav(class: themed(:nav))
|
43
|
-
render_items(@menu.items)
|
44
|
-
end
|
47
|
+
nav(class: themed(:nav)) { render_items(@menu.items) }
|
45
48
|
end
|
46
49
|
|
47
50
|
protected
|
@@ -50,14 +53,12 @@ module Phlexi
|
|
50
53
|
#
|
51
54
|
# @param items [Array<Phlexi::Menu::Item>] The items to render
|
52
55
|
# @param depth [Integer] Current nesting depth
|
56
|
+
# @return [void]
|
53
57
|
def render_items(items, depth = 0)
|
54
|
-
return if depth >= @max_depth
|
55
|
-
return if items.empty?
|
58
|
+
return if depth >= @max_depth || items.empty?
|
56
59
|
|
57
60
|
ul(class: themed(:items_container, depth)) do
|
58
|
-
items.each
|
59
|
-
render_item_wrapper(item, depth)
|
60
|
-
end
|
61
|
+
items.each { |item| render_item_wrapper(item, depth) }
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
@@ -65,35 +66,41 @@ module Phlexi
|
|
65
66
|
#
|
66
67
|
# @param item [Phlexi::Menu::Item] The item to wrap
|
67
68
|
# @param depth [Integer] Current nesting depth
|
69
|
+
# @return [void]
|
68
70
|
def render_item_wrapper(item, depth)
|
69
|
-
li(class:
|
70
|
-
themed(:item_wrapper, depth),
|
71
|
-
item_parent_class(item, depth),
|
72
|
-
active?(item) ? themed(:active, depth) : nil
|
73
|
-
)) do
|
71
|
+
li(class: compute_item_wrapper_classes(item, depth)) do
|
74
72
|
render_item_content(item, depth)
|
75
|
-
|
73
|
+
render_nested_items(item, depth)
|
76
74
|
end
|
77
75
|
end
|
78
76
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
77
|
+
# Computes CSS classes for item wrapper
|
78
|
+
#
|
79
|
+
# @param item [Phlexi::Menu::Item] The menu item
|
80
|
+
# @param depth [Integer] Current nesting depth
|
81
|
+
# @return [String] Space-separated CSS classes
|
82
|
+
def compute_item_wrapper_classes(item, depth)
|
83
|
+
wrapper = themed(:item_wrapper, depth)
|
84
|
+
parent = item_parent_class(item, depth)
|
85
|
+
active = active?(item) ? themed(:active, depth) : nil
|
84
86
|
|
85
|
-
|
86
|
-
nested?(item, depth) ? themed(:item_parent, depth) : nil
|
87
|
+
[wrapper, parent, active].compact.join(" ")
|
87
88
|
end
|
88
89
|
|
89
|
-
|
90
|
-
|
90
|
+
# Renders nested items if present and within depth limit
|
91
|
+
#
|
92
|
+
# @param item [Phlexi::Menu::Item] The parent menu item
|
93
|
+
# @param depth [Integer] Current nesting depth
|
94
|
+
# @return [void]
|
95
|
+
def render_nested_items(item, depth)
|
96
|
+
render_items(item.items, depth + 1) if nested?(item, depth)
|
91
97
|
end
|
92
98
|
|
93
99
|
# Renders the content of a menu item, choosing between link and span.
|
94
100
|
#
|
95
101
|
# @param item [Phlexi::Menu::Item] The item to render content for
|
96
102
|
# @param depth [Integer] Current nesting depth
|
103
|
+
# @return [void]
|
97
104
|
def render_item_content(item, depth)
|
98
105
|
if item.url
|
99
106
|
render_item_link(item, depth)
|
@@ -106,14 +113,13 @@ module Phlexi
|
|
106
113
|
#
|
107
114
|
# @param item [Phlexi::Menu::Item] The item to render as a link
|
108
115
|
# @param depth [Integer] Current nesting depth
|
116
|
+
# @return [void]
|
109
117
|
def render_item_link(item, depth)
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
)
|
116
|
-
) do
|
118
|
+
link_class = themed(:item_link, depth)
|
119
|
+
active = active_class(item, depth)
|
120
|
+
classes = active ? "#{link_class} #{active}" : link_class
|
121
|
+
|
122
|
+
a(href: item.url, class: classes) do
|
117
123
|
render_item_interior(item, depth)
|
118
124
|
end
|
119
125
|
end
|
@@ -122,6 +128,7 @@ module Phlexi
|
|
122
128
|
#
|
123
129
|
# @param item [Phlexi::Menu::Item] The item to render as a span
|
124
130
|
# @param depth [Integer] Current nesting depth
|
131
|
+
# @return [void]
|
125
132
|
def render_item_span(item, depth)
|
126
133
|
span(class: themed(:item_span, depth)) do
|
127
134
|
render_item_interior(item, depth)
|
@@ -132,47 +139,69 @@ module Phlexi
|
|
132
139
|
#
|
133
140
|
# @param item [Phlexi::Menu::Item] The item to render interior content for
|
134
141
|
# @param depth [Integer] Current nesting depth
|
142
|
+
# @return [void]
|
135
143
|
def render_item_interior(item, depth)
|
136
|
-
render_leading_badge(item
|
144
|
+
render_leading_badge(item, depth) if item.leading_badge
|
137
145
|
render_icon(item.icon, depth) if item.icon
|
138
146
|
render_label(item.label, depth)
|
139
|
-
render_trailing_badge(item
|
147
|
+
render_trailing_badge(item, depth) if item.trailing_badge
|
140
148
|
end
|
141
149
|
|
142
150
|
# Renders the item's label.
|
143
151
|
#
|
144
152
|
# @param label [String, Component] The label to render
|
145
153
|
# @param depth [Integer] Current nesting depth
|
154
|
+
# @return [void]
|
146
155
|
def render_label(label, depth)
|
147
|
-
phlexi_render(label)
|
156
|
+
phlexi_render(label) do
|
148
157
|
span(class: themed(:item_label, depth)) { label }
|
149
|
-
|
158
|
+
end
|
150
159
|
end
|
151
160
|
|
152
|
-
# Renders the
|
161
|
+
# Renders the leading badge if present
|
153
162
|
#
|
154
|
-
# @param
|
163
|
+
# @param item [Phlexi::Menu::Item] The menu item
|
155
164
|
# @param depth [Integer] Current nesting depth
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
165
|
+
# @return [void]
|
166
|
+
def render_leading_badge(item, depth)
|
167
|
+
return unless item.leading_badge
|
168
|
+
|
169
|
+
div(class: themed(:leading_badge_wrapper, depth)) do
|
170
|
+
render_badge(item.leading_badge, item.leading_badge_options, :leading_badge, depth)
|
171
|
+
end
|
160
172
|
end
|
161
173
|
|
162
|
-
# Renders the
|
174
|
+
# Renders the trailing badge if present
|
163
175
|
#
|
164
|
-
# @param
|
176
|
+
# @param item [Phlexi::Menu::Item] The menu item
|
165
177
|
# @param depth [Integer] Current nesting depth
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
178
|
+
# @return [void]
|
179
|
+
def render_trailing_badge(item, depth)
|
180
|
+
return unless item.trailing_badge
|
181
|
+
|
182
|
+
div(class: themed(:trailing_badge_wrapper, depth)) do
|
183
|
+
render_badge(item.trailing_badge, item.trailing_badge_options, :trailing_badge, depth)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Renders a badge with given options
|
188
|
+
#
|
189
|
+
# @param badge [Object] The badge content
|
190
|
+
# @param options [Hash] Badge rendering options
|
191
|
+
# @param type [Symbol] Badge type (leading or trailing)
|
192
|
+
# @param depth [Integer] Current nesting depth
|
193
|
+
# @return [void]
|
194
|
+
def render_badge(badge, options, type, depth)
|
195
|
+
phlexi_render(badge) do
|
196
|
+
render self.class::Badge.new(badge, **options)
|
197
|
+
end
|
170
198
|
end
|
171
199
|
|
172
200
|
# Renders the item's icon.
|
173
201
|
#
|
174
202
|
# @param icon [Class] The icon component class to render
|
175
203
|
# @param depth [Integer] Current nesting depth
|
204
|
+
# @return [void]
|
176
205
|
def render_icon(icon, depth)
|
177
206
|
return unless icon
|
178
207
|
|
@@ -205,7 +234,9 @@ module Phlexi
|
|
205
234
|
# @param depth [Integer] Current nesting depth
|
206
235
|
# @return [Boolean] Whether the item should be treated as nested
|
207
236
|
def nested?(item, depth)
|
208
|
-
item.
|
237
|
+
has_children = item.items.any?
|
238
|
+
within_depth = (depth + 1) < @max_depth
|
239
|
+
has_children && within_depth
|
209
240
|
end
|
210
241
|
|
211
242
|
# Determines the parent state class for an item.
|
@@ -221,25 +252,26 @@ module Phlexi
|
|
221
252
|
#
|
222
253
|
# @param component [Symbol] The theme component to resolve
|
223
254
|
# @param depth [Integer] Current nesting depth
|
224
|
-
# @return [String
|
255
|
+
# @return [String] The resolved CSS classes
|
225
256
|
def themed(component, depth = 0)
|
226
257
|
theme = self.class::Theme.instance.resolve_theme(component)
|
227
|
-
|
228
|
-
return theme unless theme.respond_to?(:call)
|
229
|
-
theme.call(depth)
|
258
|
+
theme.is_a?(Proc) ? theme.call(depth) : theme
|
230
259
|
end
|
231
260
|
|
232
|
-
#
|
261
|
+
# Helper method to render content with proper handling of different types
|
233
262
|
#
|
234
|
-
# @param arg [Object] The
|
235
|
-
# @yield
|
263
|
+
# @param arg [Object] The content to render
|
264
|
+
# @yield Block to render if arg is nil
|
236
265
|
# @raise [ArgumentError] If no block is provided
|
266
|
+
# @return [void]
|
237
267
|
def phlexi_render(arg, &)
|
238
268
|
return unless arg
|
239
269
|
raise ArgumentError, "phlexi_render requires a default render block" unless block_given?
|
240
270
|
|
271
|
+
# Handle Phlex components or Rails Renderables
|
241
272
|
if arg.class < Phlex::SGML || arg.respond_to?(:render_in)
|
242
273
|
render arg
|
274
|
+
# Handle procs
|
243
275
|
elsif arg.respond_to?(:to_proc)
|
244
276
|
instance_exec(&arg)
|
245
277
|
else
|
@@ -247,6 +279,7 @@ module Phlexi
|
|
247
279
|
end
|
248
280
|
end
|
249
281
|
|
282
|
+
# @return [Integer] The default maximum depth for the menu
|
250
283
|
def default_max_depth = self.class::DEFAULT_MAX_DEPTH
|
251
284
|
end
|
252
285
|
end
|
data/lib/phlexi/menu/item.rb
CHANGED
@@ -20,6 +20,11 @@ module Phlexi
|
|
20
20
|
# admin.item "Users", url: "/admin/users"
|
21
21
|
# admin.item "Settings", url: "/admin/settings"
|
22
22
|
# end
|
23
|
+
#
|
24
|
+
# @example Custom active state logic
|
25
|
+
# Item.new("Dashboard", url: "/dashboard", active: -> (context) {
|
26
|
+
# context.controller.controller_name == "dashboards"
|
27
|
+
# })
|
23
28
|
class Item
|
24
29
|
# @return [String] The display text for the menu item
|
25
30
|
attr_reader :label
|
@@ -33,9 +38,15 @@ module Phlexi
|
|
33
38
|
# @return [String, Component, nil] The badge displayed before the label
|
34
39
|
attr_reader :leading_badge
|
35
40
|
|
41
|
+
# @return [Hash] Options for the leading badge
|
42
|
+
attr_reader :leading_badge_options
|
43
|
+
|
36
44
|
# @return [String, Component, nil] The badge displayed after the label
|
37
45
|
attr_reader :trailing_badge
|
38
46
|
|
47
|
+
# @return [Hash] Options for the trailing badge
|
48
|
+
attr_reader :trailing_badge_options
|
49
|
+
|
39
50
|
# @return [Array<Item>] Collection of nested menu items
|
40
51
|
attr_reader :items
|
41
52
|
|
@@ -55,14 +66,12 @@ module Phlexi
|
|
55
66
|
# @raise [ArgumentError] If the label is nil or empty
|
56
67
|
def initialize(label, url: nil, icon: nil, leading_badge: nil, trailing_badge: nil, **options, &)
|
57
68
|
raise ArgumentError, "Label cannot be nil" unless label
|
58
|
-
|
59
69
|
@label = label
|
60
70
|
@url = url
|
61
71
|
@icon = icon
|
62
|
-
@leading_badge = leading_badge
|
63
|
-
@trailing_badge = trailing_badge
|
64
|
-
@options = options
|
65
72
|
@items = []
|
73
|
+
@options = options
|
74
|
+
setup_badges(leading_badge, trailing_badge, options)
|
66
75
|
|
67
76
|
yield self if block_given?
|
68
77
|
end
|
@@ -70,16 +79,44 @@ module Phlexi
|
|
70
79
|
# Creates and adds a nested menu item.
|
71
80
|
#
|
72
81
|
# @param label [String] The display text for the nested item
|
73
|
-
# @param
|
82
|
+
# @param args [Hash] Additional options passed to the Item constructor
|
74
83
|
# @yield [item] Optional block for adding further nested items
|
75
84
|
# @yieldparam item [Item] The newly created nested item
|
76
85
|
# @return [Item] The created nested item
|
77
|
-
def item(label,
|
78
|
-
new_item = self.class.new(label,
|
86
|
+
def item(label, **args, &)
|
87
|
+
new_item = self.class.new(label, **args, &)
|
79
88
|
@items << new_item
|
80
89
|
new_item
|
81
90
|
end
|
82
91
|
|
92
|
+
# Add a leading badge to the menu item
|
93
|
+
#
|
94
|
+
# @param badge [String, Component] The badge content
|
95
|
+
# @param opts [Hash] Additional options for the badge
|
96
|
+
# @return [self] Returns self for method chaining
|
97
|
+
# @raise [ArgumentError] If badge is nil
|
98
|
+
def with_leading_badge(badge, **opts)
|
99
|
+
raise ArgumentError, "Badge cannot be nil" if badge.nil?
|
100
|
+
|
101
|
+
@leading_badge = badge
|
102
|
+
@leading_badge_options = opts.freeze
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
# Add a trailing badge to the menu item
|
107
|
+
#
|
108
|
+
# @param badge [String, Component] The badge content
|
109
|
+
# @param opts [Hash] Additional options for the badge
|
110
|
+
# @return [self] Returns self for method chaining
|
111
|
+
# @raise [ArgumentError] If badge is nil
|
112
|
+
def with_trailing_badge(badge, **opts)
|
113
|
+
raise ArgumentError, "Badge cannot be nil" if badge.nil?
|
114
|
+
|
115
|
+
@trailing_badge = badge
|
116
|
+
@trailing_badge_options = opts.freeze
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
83
120
|
# Determines if this menu item should be shown as active.
|
84
121
|
# Checks in the following order:
|
85
122
|
# 1. Custom active logic if provided in options
|
@@ -89,44 +126,54 @@ module Phlexi
|
|
89
126
|
# @param context [Object] The context object (typically a controller) for active state checking
|
90
127
|
# @return [Boolean] true if the item should be shown as active, false otherwise
|
91
128
|
def active?(context)
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
if context.respond_to?(:helpers) && @url
|
97
|
-
return true if context.helpers.current_page?(@url)
|
98
|
-
end
|
129
|
+
check_custom_active_state(context) ||
|
130
|
+
check_current_page_match(context) ||
|
131
|
+
check_nested_items_active(context)
|
132
|
+
end
|
99
133
|
|
100
|
-
|
101
|
-
|
134
|
+
# Returns a string representation of the menu item.
|
135
|
+
#
|
136
|
+
# @return [String] A human-readable representation of the menu item
|
137
|
+
def inspect
|
138
|
+
"#<#{self.class} label=#{@label.inspect} url=#{@url.inspect} items=#{@items.inspect}>"
|
102
139
|
end
|
103
140
|
|
104
|
-
|
141
|
+
private
|
142
|
+
|
143
|
+
# Sets up the badge attributes
|
105
144
|
#
|
106
|
-
# @
|
107
|
-
|
108
|
-
|
145
|
+
# @param leading_badge [String, Component, nil] The leading badge
|
146
|
+
# @param trailing_badge [String, Component, nil] The trailing badge
|
147
|
+
# @param options [Hash] Options containing badge configurations
|
148
|
+
def setup_badges(leading_badge, trailing_badge, options)
|
149
|
+
@leading_badge = leading_badge
|
150
|
+
@leading_badge_options = (options.delete(:leading_badge_options) || {}).freeze
|
151
|
+
@trailing_badge = trailing_badge
|
152
|
+
@trailing_badge_options = (options.delete(:trailing_badge_options) || {}).freeze
|
109
153
|
end
|
110
154
|
|
111
|
-
#
|
155
|
+
# Checks if there's custom active state logic
|
112
156
|
#
|
113
|
-
# @
|
114
|
-
|
115
|
-
|
157
|
+
# @param context [Object] The context for active state checking
|
158
|
+
# @return [Boolean] Result of custom active check
|
159
|
+
def check_custom_active_state(context)
|
160
|
+
@options[:active].respond_to?(:call) && @options[:active].call(context)
|
116
161
|
end
|
117
162
|
|
118
|
-
# Checks if
|
163
|
+
# Checks if the current page matches the item's URL
|
119
164
|
#
|
120
|
-
# @
|
121
|
-
|
122
|
-
|
165
|
+
# @param context [Object] The context for URL matching
|
166
|
+
# @return [Boolean] Whether the current page matches
|
167
|
+
def check_current_page_match(context)
|
168
|
+
context.respond_to?(:helpers) && @url && context.helpers.current_page?(@url)
|
123
169
|
end
|
124
170
|
|
125
|
-
#
|
171
|
+
# Checks if any nested items are active
|
126
172
|
#
|
127
|
-
# @
|
128
|
-
|
129
|
-
|
173
|
+
# @param context [Object] The context for checking nested items
|
174
|
+
# @return [Boolean] Whether any nested items are active
|
175
|
+
def check_nested_items_active(context)
|
176
|
+
@items.any? { |item| item.active?(context) }
|
130
177
|
end
|
131
178
|
end
|
132
179
|
end
|
data/lib/phlexi/menu/theme.rb
CHANGED
@@ -25,6 +25,8 @@ module Phlexi
|
|
25
25
|
hover: nil, # Hover state
|
26
26
|
|
27
27
|
# Badge elements
|
28
|
+
leading_badge_wrapper: nil, # Wrapper for leading badge
|
29
|
+
trailing_badge_wrapper: nil, # Wrapper for trailing badge
|
28
30
|
leading_badge: nil, # Badge before label
|
29
31
|
trailing_badge: nil, # Badge after label
|
30
32
|
|
data/lib/phlexi/menu/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phlexi-menu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Froelich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: phlex
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: zeitwerk
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: phlexi-field
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.1.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.1.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -180,15 +180,15 @@ files:
|
|
180
180
|
- LICENSE.txt
|
181
181
|
- README.md
|
182
182
|
- Rakefile
|
183
|
+
- changes.patch
|
183
184
|
- config.ru
|
184
|
-
- export.json
|
185
|
-
- export.rb
|
186
185
|
- gemfiles/default.gemfile
|
187
186
|
- gemfiles/default.gemfile.lock
|
188
187
|
- gemfiles/rails_7.gemfile
|
189
188
|
- gemfiles/rails_7.gemfile.lock
|
190
189
|
- lib/phlexi-menu.rb
|
191
190
|
- lib/phlexi/menu.rb
|
191
|
+
- lib/phlexi/menu/badge.rb
|
192
192
|
- lib/phlexi/menu/builder.rb
|
193
193
|
- lib/phlexi/menu/component.rb
|
194
194
|
- lib/phlexi/menu/item.rb
|