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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77a15ed27fda6bba3dafc46555f7f796b94a18982056fb3898d73393539a02c1
|
4
|
+
data.tar.gz: d91ecb5053d3935209c2c68099b5615db195994da9f1ae9236bc5a9f993267e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc8f8b414638e5f5960f763978f3cb6e3c59ec7e745e6def67cf53a7d77c715e6b2331f3dd42aa4631279d51f06844cb2ca331557671f5fcee2128c9fc2c3f7d
|
7
|
+
data.tar.gz: 0d4c58a1ce2738b39ab32497a00688d0290293b844c43539418fa051339d9ba52439a87aeb59f8bcc112978e57a7f9d8cf7feb17880dcc509a0d4c455156b325
|
data/README.md
CHANGED
@@ -12,12 +12,20 @@ Phlexi::Menu is a flexible and powerful menu builder for Ruby applications. It p
|
|
12
12
|
- [Usage](#usage)
|
13
13
|
- [Basic Usage](#basic-usage)
|
14
14
|
- [Menu Items](#menu-items)
|
15
|
+
- [Badge System](#badge-system)
|
15
16
|
- [Component Options](#component-options)
|
17
|
+
- [Nesting and Depth Limits](#nesting-and-depth-limits)
|
16
18
|
- [Theming](#theming)
|
17
|
-
|
19
|
+
- [Static Theming](#static-theming)
|
20
|
+
- [Depth-Aware Theming](#depth-aware-theming)
|
18
21
|
- [Rails Integration](#rails-integration)
|
19
22
|
- [Advanced Usage](#advanced-usage)
|
23
|
+
- [Active State Detection](#active-state-detection)
|
20
24
|
- [Component Customization](#component-customization)
|
25
|
+
- [Core Rendering Methods](#core-rendering-methods)
|
26
|
+
- [Badge Related Methods](#badge-related-methods)
|
27
|
+
- [Other Components](#other-components)
|
28
|
+
- [Helper Methods](#helper-methods)
|
21
29
|
- [Dynamic Menus](#dynamic-menus)
|
22
30
|
- [Development](#development)
|
23
31
|
- [Contributing](#contributing)
|
@@ -25,10 +33,11 @@ Phlexi::Menu is a flexible and powerful menu builder for Ruby applications. It p
|
|
25
33
|
|
26
34
|
## Features
|
27
35
|
|
28
|
-
- Hierarchical menu structure with
|
29
|
-
-
|
36
|
+
- Hierarchical menu structure with intelligent depth control
|
37
|
+
- Enhanced badge system with customizable options and wrappers
|
30
38
|
- Intelligent active state detection
|
31
|
-
- Flexible theming system
|
39
|
+
- Flexible theming system with depth awareness
|
40
|
+
- Smart nesting behavior based on depth limits
|
32
41
|
- Works seamlessly with Phlex components
|
33
42
|
- Rails-compatible URL handling
|
34
43
|
- Customizable rendering components
|
@@ -64,12 +73,15 @@ class MainMenu < Phlexi::Menu::Component
|
|
64
73
|
super.merge({
|
65
74
|
nav: "bg-white shadow",
|
66
75
|
items_container: "space-y-1",
|
67
|
-
item_wrapper: "relative",
|
76
|
+
item_wrapper: ->(depth) { "relative pl-#{depth * 4}" },
|
68
77
|
item_link: "flex items-center px-4 py-2 hover:bg-gray-50",
|
69
|
-
|
70
|
-
|
78
|
+
item_label: ->(depth) { "mx-3 text-gray-#{600 + (depth * 100)}" },
|
79
|
+
# Badge wrapper styles
|
80
|
+
leading_badge_wrapper: "flex items-center",
|
81
|
+
trailing_badge_wrapper: "flex items-center ml-auto",
|
82
|
+
# Badge styles
|
71
83
|
leading_badge: "mr-2 px-2 py-0.5 text-xs rounded-full bg-blue-100 text-blue-600",
|
72
|
-
trailing_badge: "
|
84
|
+
trailing_badge: "px-2 py-0.5 text-xs rounded-full bg-red-100 text-red-600",
|
73
85
|
icon: "h-5 w-5",
|
74
86
|
active: "bg-blue-50 text-blue-600"
|
75
87
|
})
|
@@ -79,22 +91,20 @@ end
|
|
79
91
|
|
80
92
|
# Using the menu
|
81
93
|
menu = Phlexi::Menu::Builder.new do |m|
|
82
|
-
m.item "Dashboard",
|
83
|
-
url: "/",
|
84
|
-
icon: DashboardIcon
|
94
|
+
m.item "Dashboard", url: "/", icon: DashboardIcon
|
85
95
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
96
|
+
# Using the new fluent badge API
|
97
|
+
m.item "Users", url: "/users"
|
98
|
+
.with_leading_badge("Beta", color: "blue")
|
99
|
+
.with_trailing_badge("23", size: "sm") do |users|
|
90
100
|
users.item "All Users", url: "/users"
|
91
101
|
users.item "Add User", url: "/users/new"
|
92
102
|
end
|
93
|
-
|
103
|
+
|
94
104
|
m.item "Settings",
|
95
105
|
url: "/settings",
|
96
106
|
icon: SettingsIcon,
|
97
|
-
leading_badge:
|
107
|
+
leading_badge: StatusBadge.new(type: "warning")
|
98
108
|
end
|
99
109
|
|
100
110
|
# In your view
|
@@ -116,6 +126,35 @@ m.item "Menu Item",
|
|
116
126
|
}
|
117
127
|
```
|
118
128
|
|
129
|
+
The new fluent badge API provides a cleaner way to add badges:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
m.item "Products"
|
133
|
+
.with_leading_badge("New", class: "text-blue-900")
|
134
|
+
.with_trailing_badge("99+", class: "text-sm")
|
135
|
+
```
|
136
|
+
|
137
|
+
### Badge System
|
138
|
+
|
139
|
+
The enhanced badge system supports both simple text badges and complex component badges with customization options:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
# Simple text badges with options
|
143
|
+
m.item "Products"
|
144
|
+
.with_leading_badge("New", class: "text-green-400")
|
145
|
+
|
146
|
+
# Component badges
|
147
|
+
m.item "Messages"
|
148
|
+
.with_leading_badge(StatusBadge.new(status: "active"))
|
149
|
+
.with_trailing_badge(CounterBadge.new(count: 3))
|
150
|
+
|
151
|
+
# Legacy style still supported
|
152
|
+
m.item "Legacy",
|
153
|
+
leading_badge: "Beta",
|
154
|
+
trailing_badge: "2",
|
155
|
+
leading_badge_options: { class: "text-green-400"},
|
156
|
+
```
|
157
|
+
|
119
158
|
### Component Options
|
120
159
|
|
121
160
|
The menu component accepts these initialization options:
|
@@ -128,8 +167,52 @@ MainMenu.new(
|
|
128
167
|
)
|
129
168
|
```
|
130
169
|
|
170
|
+
### Nesting and Depth Limits
|
171
|
+
|
172
|
+
Phlexi::Menu intelligently handles menu nesting based on the specified maximum depth:
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
# Create a deeply nested menu structure
|
176
|
+
menu = Phlexi::Menu::Builder.new do |m|
|
177
|
+
m.item "Level 0" do |l0| # Will be nested (depth 0)
|
178
|
+
l0.item "Level 1" do |l1| # Will be nested if max_depth > 2
|
179
|
+
l1.item "Level 2" do |l2| # Will be nested if max_depth > 3
|
180
|
+
l2.item "Level 3"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Render with depth limit
|
187
|
+
menu_component = MainMenu.new(menu, max_depth: 2)
|
188
|
+
```
|
189
|
+
|
131
190
|
### Theming
|
132
191
|
|
192
|
+
The theming system now includes dedicated wrapper elements for badges:
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
def self.theme
|
196
|
+
super.merge({
|
197
|
+
# Badge containers
|
198
|
+
leading_badge_wrapper: "flex items-center",
|
199
|
+
trailing_badge_wrapper: "ml-auto",
|
200
|
+
|
201
|
+
# Badge elements
|
202
|
+
leading_badge: ->(depth) {
|
203
|
+
["badge", depth.zero? ? "primary" : "secondary"]
|
204
|
+
},
|
205
|
+
trailing_badge: ->(depth) {
|
206
|
+
["badge", "ml-2", "level-#{depth}"]
|
207
|
+
}
|
208
|
+
})
|
209
|
+
end
|
210
|
+
```
|
211
|
+
|
212
|
+
#### Static Theming
|
213
|
+
|
214
|
+
Basic theme configuration with fixed classes:
|
215
|
+
|
133
216
|
```ruby
|
134
217
|
class CustomMenu < Phlexi::Menu::Component
|
135
218
|
class Theme < Theme
|
@@ -151,24 +234,31 @@ class CustomMenu < Phlexi::Menu::Component
|
|
151
234
|
end
|
152
235
|
```
|
153
236
|
|
154
|
-
|
237
|
+
#### Depth-Aware Theming
|
155
238
|
|
156
|
-
|
239
|
+
Advanced theme configuration with depth-sensitive classes:
|
157
240
|
|
158
241
|
```ruby
|
159
|
-
class
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
242
|
+
class DepthAwareMenu < Phlexi::Menu::Component
|
243
|
+
class Theme < Theme
|
244
|
+
def self.theme
|
245
|
+
super.merge({
|
246
|
+
item_wrapper: ->(depth) { "relative pl-#{depth * 4}" },
|
247
|
+
item_label: ->(depth) { "mx-3 text-gray-#{600 + (depth * 100)}" },
|
248
|
+
leading_badge: ->(depth) {
|
249
|
+
["badge", "mr-2", depth.zero? ? "primary" : "secondary"]
|
250
|
+
}
|
251
|
+
})
|
164
252
|
end
|
165
253
|
end
|
166
254
|
end
|
167
|
-
|
168
|
-
# Usage
|
169
|
-
m.item "Products", leading_badge: CustomBadgeComponent.new
|
170
255
|
```
|
171
256
|
|
257
|
+
Theme values can be either:
|
258
|
+
- Static strings for consistent styling
|
259
|
+
- Arrays of classes that will be joined
|
260
|
+
- Callables (procs/lambdas) that receive the current depth and return strings or arrays
|
261
|
+
|
172
262
|
### Rails Integration
|
173
263
|
|
174
264
|
In your controller:
|
@@ -193,8 +283,8 @@ class ApplicationController < ActionController::Base
|
|
193
283
|
|
194
284
|
if current_user&.admin?
|
195
285
|
m.item "Admin",
|
196
|
-
url: admin_path
|
197
|
-
|
286
|
+
url: admin_path
|
287
|
+
.with_leading_badge("Admin", variant: "warning")
|
198
288
|
end
|
199
289
|
end
|
200
290
|
end
|
@@ -202,50 +292,97 @@ class ApplicationController < ActionController::Base
|
|
202
292
|
end
|
203
293
|
```
|
204
294
|
|
205
|
-
|
295
|
+
## Advanced Usage
|
296
|
+
|
297
|
+
### Active State Detection
|
298
|
+
|
299
|
+
The menu system provides multiple ways to determine the active state of items:
|
206
300
|
|
207
301
|
```ruby
|
208
|
-
m.item "Custom Active",
|
209
|
-
|
210
|
-
context
|
211
|
-
|
302
|
+
m.item "Custom Active",
|
303
|
+
url: "/path",
|
304
|
+
active: ->(context) {
|
305
|
+
# Custom active state logic
|
306
|
+
context.request.path.start_with?("/path")
|
307
|
+
}
|
212
308
|
```
|
213
309
|
|
214
|
-
|
310
|
+
Default behavior checks:
|
311
|
+
1. Custom active logic (if provided)
|
312
|
+
2. Current page match
|
313
|
+
3. Active state of child items
|
215
314
|
|
216
315
|
### Component Customization
|
217
316
|
|
218
|
-
You can customize specific rendering steps
|
317
|
+
You can customize specific rendering steps by subclassing the base component and overriding specific methods.
|
318
|
+
|
319
|
+
The component provides these customization points:
|
320
|
+
|
321
|
+
#### Core Rendering Methods
|
322
|
+
- `render_items(items, depth)`: Handles collection of items and nesting
|
323
|
+
- `render_item_wrapper(item, depth)`: Wraps individual items in list elements
|
324
|
+
- `render_item_content(item, depth)`: Chooses between link and span rendering
|
325
|
+
- `render_item_interior(item, depth)`: Handles the item's internal layout
|
326
|
+
|
327
|
+
#### Badge Related Methods
|
328
|
+
- `render_leading_badge(item, depth)`: Renders the item's leading badge with wrapper
|
329
|
+
- `render_trailing_badge(item, depth)`: Renders the item's trailing badge with wrapper
|
330
|
+
- `render_badge(badge, options, type, depth)`: Core badge rendering with options support
|
331
|
+
|
332
|
+
#### Other Components
|
333
|
+
- `render_icon(icon, depth)`: Renders the icon component
|
334
|
+
- `render_label(label, depth)`: Renders the item's label
|
335
|
+
|
336
|
+
#### Helper Methods
|
337
|
+
- `nested?(item, depth)`: Determines if an item should show nested children
|
338
|
+
- `active?(item)`: Determines item's active state
|
339
|
+
- `active_class(item, depth)`: Resolves active state styling
|
340
|
+
- `themed(component, depth)`: Resolves theme values for components
|
341
|
+
- `compute_item_wrapper_classes(item, depth)`: Computes wrapper CSS classes
|
342
|
+
|
343
|
+
Each method receives the current depth as a parameter for depth-aware rendering and theming. You can override any combination of these methods to customize the rendering behavior:
|
219
344
|
|
220
345
|
```ruby
|
221
346
|
class CustomMenu < Phlexi::Menu::Component
|
222
|
-
#
|
223
|
-
def
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
render_trailing_badge(item.trailing_badge) if item.trailing_badge
|
347
|
+
# Customize just the badge rendering
|
348
|
+
def render_badge(badge, options, type, depth)
|
349
|
+
if badge.is_a?(String) && type == :leading_badge
|
350
|
+
render_text_badge(badge, options, depth)
|
351
|
+
else
|
352
|
+
super
|
229
353
|
end
|
230
354
|
end
|
231
355
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
356
|
+
private
|
357
|
+
|
358
|
+
def render_text_badge(text, options, depth)
|
359
|
+
span(class: themed(:leading_badge, depth)) do
|
360
|
+
span(class: "dot") { "•" }
|
361
|
+
text
|
236
362
|
end
|
237
363
|
end
|
238
364
|
end
|
239
365
|
```
|
240
366
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
367
|
+
For Rails applications, you can also integrate with helpers and routes:
|
368
|
+
|
369
|
+
```ruby
|
370
|
+
class ApplicationMenu < Phlexi::Menu::Component
|
371
|
+
protected
|
372
|
+
|
373
|
+
def active?(item)
|
374
|
+
return super unless helpers&.respond_to?(:current_page?)
|
375
|
+
current_page?(item.url) || item.items.any? { |child| active?(child) }
|
376
|
+
end
|
377
|
+
|
378
|
+
def render_icon(icon, depth)
|
379
|
+
return super unless icon.respond_to?(:to_svg)
|
380
|
+
raw icon.to_svg(class: themed(:icon, depth))
|
381
|
+
end
|
382
|
+
end
|
383
|
+
```
|
384
|
+
|
385
|
+
The component's modular design allows you to customize exactly what you need while maintaining the core menu functionality.
|
249
386
|
|
250
387
|
### Dynamic Menus
|
251
388
|
|
@@ -267,7 +404,10 @@ Phlexi::Menu::Builder.new do |m|
|
|
267
404
|
|
268
405
|
# Dynamic items from database
|
269
406
|
current_user.organizations.each do |org|
|
270
|
-
m.item org.name,
|
407
|
+
m.item org.name,
|
408
|
+
url: organization_path(org),
|
409
|
+
icon: OrgIcon,
|
410
|
+
trailing_badge: org.unread_notifications_count
|
271
411
|
end
|
272
412
|
end
|
273
413
|
```
|