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 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
  ```