phlexi-menu 0.0.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7cc4d5883a4c76ae7455de7f38c33639dccee507b1406549a00e1f211906020e
4
- data.tar.gz: 23332831a113a1dc53515afe6449861cb8282b2f1ecd2f4dfde1eaf427cb0d52
3
+ metadata.gz: 77a15ed27fda6bba3dafc46555f7f796b94a18982056fb3898d73393539a02c1
4
+ data.tar.gz: d91ecb5053d3935209c2c68099b5615db195994da9f1ae9236bc5a9f993267e5
5
5
  SHA512:
6
- metadata.gz: ab3e7aad92d888a87599f33fcffa5e52c413e8b7bffaba4852e4d07e2fdd65e1b3dbf3f031542f95995a78fbac3fd29cf4609db7c332581af23249f7b07f84c2
7
- data.tar.gz: bd393a7fa7bdc00f2c20ee7227bbde090584d87f2c310cdf07d77c1cf4607871142860223f1ca267bfeedfb40bb724b3d88f04df89ef3f4eac6c80a62c59cf2a
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
- - [Badge Components](#badge-components)
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 controlled nesting depth
29
- - Support for icons and dual-badge system (leading and trailing badges)
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
- item_span: "flex items-center px-4 py-2",
70
- item_label: "mx-3",
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: "ml-auto px-2 py-0.5 text-xs rounded-full bg-red-100 text-red-600",
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
- m.item "Users",
87
- url: "/users",
88
- leading_badge: "Beta",
89
- trailing_badge: "23" do |users|
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: CustomBadgeComponent.new
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
- ### Badge Components
237
+ #### Depth-Aware Theming
155
238
 
156
- Badges can be either strings or Phlex components:
239
+ Advanced theme configuration with depth-sensitive classes:
157
240
 
158
241
  ```ruby
159
- class CustomBadgeComponent < ApplicationComponent
160
- def view_template
161
- div(class: "flex items-center") do
162
- span(class: "h-2 w-2 rounded-full bg-blue-400")
163
- span(class: "ml-2") { "New" }
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
- leading_badge: "Admin"
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
- Note: The menu component uses Rails' `current_page?` helper for default active state detection. If you're not using Rails or want custom active state logic, provide an `active` callable to your menu items:
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", url: "/path", active: ->(context) {
209
- # Your custom active state logic here
210
- context.request.path.start_with?("/path")
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
- ## Advanced Usage
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
- # Override just what you need
223
- def render_item_interior(item)
224
- div(class: "flex items-center gap-2") do
225
- render_leading_badge(item.leading_badge) if item.leading_badge
226
- render_icon(item.icon) if item.icon
227
- span(class: themed(:item_label)) { item.label.upcase }
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
- def render_leading_badge(badge)
233
- div(class: tokens(themed(:leading_badge), "flex items-center")) do
234
- span { "●" }
235
- span(class: "ml-1") { badge }
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
- The component provides these customization points:
242
- - `render_items`: Handles collection of items and nesting
243
- - `render_item_wrapper`: Wraps individual items
244
- - `render_item_content`: Chooses between link and span rendering
245
- - `render_item_interior`: Handles the item's internal layout
246
- - `render_leading_badge`: Renders the leading badge
247
- - `render_trailing_badge`: Renders the trailing badge
248
- - `render_icon`: Renders the icon component
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, url: organization_path(org), icon: OrgIcon
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
  ```