phlexi-menu 0.0.3 → 0.2.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/README.md +196 -56
- data/changes.patch +393 -0
- data/export.json +7 -7
- 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 +140 -59
- data/lib/phlexi/menu/item.rb +79 -32
- data/lib/phlexi/menu/theme.rb +28 -11
- data/lib/phlexi/menu/version.rb +1 -1
- metadata +4 -2
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "phlex"
|
4
|
+
|
5
|
+
module Phlexi
|
6
|
+
module Menu
|
7
|
+
# A component for rendering badge elements in menus
|
8
|
+
#
|
9
|
+
# @example Basic usage
|
10
|
+
# Badge.new("New!", class: "badge-primary")
|
11
|
+
#
|
12
|
+
# @example With custom styling
|
13
|
+
# Badge.new("2", class: "badge-notification")
|
14
|
+
#
|
15
|
+
class Badge < COMPONENT_BASE
|
16
|
+
# Initialize a new badge component
|
17
|
+
#
|
18
|
+
# @param content [String] The text content to display in the badge
|
19
|
+
# @param options [Hash] Additional HTML attributes for the badge element
|
20
|
+
# @option options [String] :class CSS classes to apply to the badge
|
21
|
+
def initialize(content, **options)
|
22
|
+
@content = content
|
23
|
+
@options = options
|
24
|
+
super()
|
25
|
+
end
|
26
|
+
|
27
|
+
def view_template
|
28
|
+
span(class: @options[:class]) { @content }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/phlexi/menu/builder.rb
CHANGED
@@ -47,27 +47,6 @@ module Phlexi
|
|
47
47
|
new_item
|
48
48
|
end
|
49
49
|
|
50
|
-
# Checks if the menu has any items.
|
51
|
-
#
|
52
|
-
# @return [Boolean] true if the menu has no items, false otherwise
|
53
|
-
def empty?
|
54
|
-
@items.empty?
|
55
|
-
end
|
56
|
-
|
57
|
-
# Returns the number of top-level items in the menu.
|
58
|
-
#
|
59
|
-
# @return [Integer] The count of top-level menu items
|
60
|
-
def size
|
61
|
-
@items.size
|
62
|
-
end
|
63
|
-
|
64
|
-
# Checks if this menu item has any nested items.
|
65
|
-
#
|
66
|
-
# @return [Boolean] true if the item has nested items, false otherwise
|
67
|
-
def nested?
|
68
|
-
!empty?
|
69
|
-
end
|
70
|
-
|
71
50
|
# Returns a string representation of the menu structure.
|
72
51
|
#
|
73
52
|
# @return [String] A human-readable representation of the menu
|
@@ -14,7 +14,7 @@ module Phlexi
|
|
14
14
|
# def self.theme
|
15
15
|
# super.merge({
|
16
16
|
# nav: "bg-white shadow",
|
17
|
-
# item_label: "text-gray
|
17
|
+
# item_label: ->(depth) { "text-gray-#{600 + (depth * 100)}" }
|
18
18
|
# })
|
19
19
|
# end
|
20
20
|
# end
|
@@ -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,20 +33,18 @@ 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
|
38
43
|
super()
|
39
44
|
end
|
40
45
|
|
41
|
-
# Renders the menu structure as HTML.
|
42
|
-
#
|
43
|
-
# @return [String] The rendered HTML
|
44
46
|
def view_template
|
45
|
-
nav(class: themed(:nav))
|
46
|
-
render_items(@menu.items)
|
47
|
-
end
|
47
|
+
nav(class: themed(:nav)) { render_items(@menu.items) }
|
48
48
|
end
|
49
49
|
|
50
50
|
protected
|
@@ -53,14 +53,12 @@ module Phlexi
|
|
53
53
|
#
|
54
54
|
# @param items [Array<Phlexi::Menu::Item>] The items to render
|
55
55
|
# @param depth [Integer] Current nesting depth
|
56
|
+
# @return [void]
|
56
57
|
def render_items(items, depth = 0)
|
57
|
-
return if depth >= @max_depth
|
58
|
-
return if items.empty?
|
58
|
+
return if depth >= @max_depth || items.empty?
|
59
59
|
|
60
|
-
ul(class: themed(:items_container)) do
|
61
|
-
items.each
|
62
|
-
render_item_wrapper(item, depth)
|
63
|
-
end
|
60
|
+
ul(class: themed(:items_container, depth)) do
|
61
|
+
items.each { |item| render_item_wrapper(item, depth) }
|
64
62
|
end
|
65
63
|
end
|
66
64
|
|
@@ -68,116 +66,197 @@ module Phlexi
|
|
68
66
|
#
|
69
67
|
# @param item [Phlexi::Menu::Item] The item to wrap
|
70
68
|
# @param depth [Integer] Current nesting depth
|
69
|
+
# @return [void]
|
71
70
|
def render_item_wrapper(item, depth)
|
72
|
-
li(class:
|
73
|
-
|
74
|
-
|
75
|
-
item_parent_class(item)
|
76
|
-
)) do
|
77
|
-
render_item_content(item)
|
78
|
-
render_items(item.items, depth + 1) if item.items.any?
|
71
|
+
li(class: compute_item_wrapper_classes(item, depth)) do
|
72
|
+
render_item_content(item, depth)
|
73
|
+
render_nested_items(item, depth)
|
79
74
|
end
|
80
75
|
end
|
81
76
|
|
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
|
+
tokens(
|
84
|
+
themed(:item_wrapper, depth),
|
85
|
+
item_parent_class(item, depth),
|
86
|
+
active?(item) ? themed(:active, depth) : nil
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
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)
|
97
|
+
end
|
98
|
+
|
82
99
|
# Renders the content of a menu item, choosing between link and span.
|
83
100
|
#
|
84
101
|
# @param item [Phlexi::Menu::Item] The item to render content for
|
85
|
-
|
102
|
+
# @param depth [Integer] Current nesting depth
|
103
|
+
# @return [void]
|
104
|
+
def render_item_content(item, depth)
|
86
105
|
if item.url
|
87
|
-
render_item_link(item)
|
106
|
+
render_item_link(item, depth)
|
88
107
|
else
|
89
|
-
render_item_span(item)
|
108
|
+
render_item_span(item, depth)
|
90
109
|
end
|
91
110
|
end
|
92
111
|
|
93
112
|
# Renders a menu item as a link.
|
94
113
|
#
|
95
114
|
# @param item [Phlexi::Menu::Item] The item to render as a link
|
96
|
-
|
97
|
-
|
98
|
-
|
115
|
+
# @param depth [Integer] Current nesting depth
|
116
|
+
# @return [void]
|
117
|
+
def render_item_link(item, depth)
|
118
|
+
a(
|
119
|
+
href: item.url,
|
120
|
+
class: tokens(themed(:item_link, depth), active_class(item, depth))
|
121
|
+
) do
|
122
|
+
render_item_interior(item, depth)
|
99
123
|
end
|
100
124
|
end
|
101
125
|
|
102
126
|
# Renders a menu item as a span (for non-linking items).
|
103
127
|
#
|
104
128
|
# @param item [Phlexi::Menu::Item] The item to render as a span
|
105
|
-
|
106
|
-
|
107
|
-
|
129
|
+
# @param depth [Integer] Current nesting depth
|
130
|
+
# @return [void]
|
131
|
+
def render_item_span(item, depth)
|
132
|
+
span(class: themed(:item_span, depth)) do
|
133
|
+
render_item_interior(item, depth)
|
108
134
|
end
|
109
135
|
end
|
110
136
|
|
111
137
|
# Renders the interior content of a menu item (badges, icon, label).
|
112
138
|
#
|
113
139
|
# @param item [Phlexi::Menu::Item] The item to render interior content for
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
140
|
+
# @param depth [Integer] Current nesting depth
|
141
|
+
# @return [void]
|
142
|
+
def render_item_interior(item, depth)
|
143
|
+
render_leading_badge(item, depth) if item.leading_badge
|
144
|
+
render_icon(item.icon, depth) if item.icon
|
145
|
+
render_label(item.label, depth)
|
146
|
+
render_trailing_badge(item, depth) if item.trailing_badge
|
119
147
|
end
|
120
148
|
|
121
149
|
# Renders the item's label.
|
122
150
|
#
|
123
151
|
# @param label [String, Component] The label to render
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
152
|
+
# @param depth [Integer] Current nesting depth
|
153
|
+
# @return [void]
|
154
|
+
def render_label(label, depth)
|
155
|
+
phlexi_render(label) do
|
156
|
+
span(class: themed(:item_label, depth)) { label }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Renders the leading badge if present
|
161
|
+
#
|
162
|
+
# @param item [Phlexi::Menu::Item] The menu item
|
163
|
+
# @param depth [Integer] Current nesting depth
|
164
|
+
# @return [void]
|
165
|
+
def render_leading_badge(item, depth)
|
166
|
+
return unless item.leading_badge
|
167
|
+
|
168
|
+
div(class: themed(:leading_badge_wrapper, depth)) do
|
169
|
+
render_badge(item.leading_badge, item.leading_badge_options, :leading_badge, depth)
|
170
|
+
end
|
128
171
|
end
|
129
172
|
|
130
|
-
# Renders the
|
173
|
+
# Renders the trailing badge if present
|
131
174
|
#
|
132
|
-
# @param
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
175
|
+
# @param item [Phlexi::Menu::Item] The menu item
|
176
|
+
# @param depth [Integer] Current nesting depth
|
177
|
+
# @return [void]
|
178
|
+
def render_trailing_badge(item, depth)
|
179
|
+
return unless item.trailing_badge
|
180
|
+
|
181
|
+
div(class: themed(:trailing_badge_wrapper, depth)) do
|
182
|
+
render_badge(item.trailing_badge, item.trailing_badge_options, :trailing_badge, depth)
|
183
|
+
end
|
137
184
|
end
|
138
185
|
|
139
|
-
# Renders
|
186
|
+
# Renders a badge with given options
|
140
187
|
#
|
141
|
-
# @param badge [
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
188
|
+
# @param badge [Object] The badge content
|
189
|
+
# @param options [Hash] Badge rendering options
|
190
|
+
# @param type [Symbol] Badge type (leading or trailing)
|
191
|
+
# @param depth [Integer] Current nesting depth
|
192
|
+
# @return [void]
|
193
|
+
def render_badge(badge, options, type, depth)
|
194
|
+
phlexi_render(badge) do
|
195
|
+
render self.class::Badge.new(badge, **options)
|
196
|
+
end
|
146
197
|
end
|
147
198
|
|
148
199
|
# Renders the item's icon.
|
149
200
|
#
|
150
201
|
# @param icon [Class] The icon component class to render
|
151
|
-
|
202
|
+
# @param depth [Integer] Current nesting depth
|
203
|
+
# @return [void]
|
204
|
+
def render_icon(icon, depth)
|
152
205
|
return unless icon
|
153
206
|
|
154
|
-
div(class: themed(:icon_wrapper)) do
|
155
|
-
render icon.new(class: themed(:icon))
|
207
|
+
div(class: themed(:icon_wrapper, depth)) do
|
208
|
+
render icon.new(class: themed(:icon, depth))
|
156
209
|
end
|
157
210
|
end
|
158
211
|
|
159
212
|
# Determines the active state class for an item.
|
160
213
|
#
|
161
214
|
# @param item [Phlexi::Menu::Item] The item to check active state for
|
215
|
+
# @param depth [Integer] Current nesting depth
|
162
216
|
# @return [String, nil] The active class name or nil
|
163
|
-
def active_class(item)
|
164
|
-
|
217
|
+
def active_class(item, depth)
|
218
|
+
active?(item) ? themed(:active, depth) : nil
|
219
|
+
end
|
220
|
+
|
221
|
+
# Helper method to check if an item is active
|
222
|
+
#
|
223
|
+
# @param item [Phlexi::Menu::Item] The item to check
|
224
|
+
# @return [Boolean] Whether the item is active
|
225
|
+
def active?(item)
|
226
|
+
item.active?(self)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Determines if an item should be treated as nested based on its contents
|
230
|
+
# and the current depth relative to the maximum allowed depth.
|
231
|
+
#
|
232
|
+
# @param item [Phlexi::Menu::Item] The item to check
|
233
|
+
# @param depth [Integer] Current nesting depth
|
234
|
+
# @return [Boolean] Whether the item should be treated as nested
|
235
|
+
def nested?(item, depth)
|
236
|
+
has_children = item.items.any?
|
237
|
+
within_depth = (depth + 1) < @max_depth
|
238
|
+
has_children && within_depth
|
165
239
|
end
|
166
240
|
|
167
241
|
# Determines the parent state class for an item.
|
168
242
|
#
|
169
243
|
# @param item [Phlexi::Menu::Item] The item to check parent state for
|
244
|
+
# @param depth [Integer] Current nesting depth
|
170
245
|
# @return [String, nil] The parent class name or nil
|
171
|
-
def item_parent_class(item)
|
172
|
-
item
|
246
|
+
def item_parent_class(item, depth)
|
247
|
+
nested?(item, depth) ? themed(:item_parent, depth) : nil
|
173
248
|
end
|
174
249
|
|
175
250
|
# Resolves a theme component to its CSS classes.
|
176
251
|
#
|
177
252
|
# @param component [Symbol] The theme component to resolve
|
253
|
+
# @param depth [Integer] Current nesting depth
|
178
254
|
# @return [String, nil] The resolved CSS classes or nil
|
179
|
-
def themed(component)
|
180
|
-
self.class::Theme.instance.resolve_theme(component)
|
255
|
+
def themed(component, depth = 0)
|
256
|
+
theme = self.class::Theme.instance.resolve_theme(component)
|
257
|
+
return nil if theme.nil?
|
258
|
+
return theme unless theme.respond_to?(:call)
|
259
|
+
theme.call(depth)
|
181
260
|
end
|
182
261
|
|
183
262
|
# Renders either a component or simple value with fallback.
|
@@ -185,6 +264,7 @@ module Phlexi
|
|
185
264
|
# @param arg [Object] The value to render
|
186
265
|
# @yield The default rendering block
|
187
266
|
# @raise [ArgumentError] If no block is provided
|
267
|
+
# @return [void]
|
188
268
|
def phlexi_render(arg, &)
|
189
269
|
return unless arg
|
190
270
|
raise ArgumentError, "phlexi_render requires a default render block" unless block_given?
|
@@ -198,6 +278,7 @@ module Phlexi
|
|
198
278
|
end
|
199
279
|
end
|
200
280
|
|
281
|
+
# @return [Integer] The default maximum depth for the menu
|
201
282
|
def default_max_depth = self.class::DEFAULT_MAX_DEPTH
|
202
283
|
end
|
203
284
|
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
@@ -3,19 +3,36 @@ require "phlexi-field"
|
|
3
3
|
module Phlexi
|
4
4
|
module Menu
|
5
5
|
class Theme < Phlexi::Field::Theme
|
6
|
+
# Defines the default theme structure with nil values
|
7
|
+
# Can be overridden in subclasses to provide custom styling
|
8
|
+
#
|
9
|
+
# @return [Hash] Default theme structure with nil values
|
6
10
|
def self.theme
|
7
11
|
@theme ||= {
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
# Container elements
|
13
|
+
nav: nil, # Navigation wrapper
|
14
|
+
items_container: nil, # <ul> list container
|
15
|
+
|
16
|
+
# Item structure elements
|
17
|
+
item_wrapper: nil, # <li> item wrapper
|
18
|
+
item_parent: nil, # Additional class for items with visible children
|
19
|
+
item_link: nil, # <a> for clickable items
|
20
|
+
item_span: nil, # <span> for non-clickable items
|
21
|
+
item_label: nil, # Label text wrapper
|
22
|
+
|
23
|
+
# Interactive states
|
24
|
+
active: nil, # Active/selected state
|
25
|
+
hover: nil, # Hover state
|
26
|
+
|
27
|
+
# Badge elements
|
28
|
+
leading_badge_wrapper: nil, # Wrapper for leading badge
|
29
|
+
trailing_badge_wrapper: nil, # Wrapper for trailing badge
|
30
|
+
leading_badge: nil, # Badge before label
|
31
|
+
trailing_badge: nil, # Badge after label
|
32
|
+
|
33
|
+
# Icon elements
|
34
|
+
icon: nil, # Icon styling
|
35
|
+
icon_wrapper: nil # Icon container
|
19
36
|
}.freeze
|
20
37
|
end
|
21
38
|
end
|
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.0
|
4
|
+
version: 0.2.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: 2024-12-
|
11
|
+
date: 2024-12-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: phlex
|
@@ -180,6 +180,7 @@ files:
|
|
180
180
|
- LICENSE.txt
|
181
181
|
- README.md
|
182
182
|
- Rakefile
|
183
|
+
- changes.patch
|
183
184
|
- config.ru
|
184
185
|
- export.json
|
185
186
|
- export.rb
|
@@ -189,6 +190,7 @@ files:
|
|
189
190
|
- gemfiles/rails_7.gemfile.lock
|
190
191
|
- lib/phlexi-menu.rb
|
191
192
|
- lib/phlexi/menu.rb
|
193
|
+
- lib/phlexi/menu/badge.rb
|
192
194
|
- lib/phlexi/menu/builder.rb
|
193
195
|
- lib/phlexi/menu/component.rb
|
194
196
|
- lib/phlexi/menu/item.rb
|