|
1 | | -module ActiveAdmin |
| 1 | +require 'active_admin/view_helpers/method_or_proc_helper' |
2 | 2 |
|
| 3 | +module ActiveAdmin |
3 | 4 | class MenuItem |
4 | 5 | include Menu::MenuNode |
| 6 | + include MethodOrProcHelper |
5 | 7 |
|
6 | | - attr_accessor :id, :label, :url, :priority, :display_if_block, :html_options |
7 | | - attr_reader :parent |
| 8 | + attr_reader :html_options, :parent, :priority |
8 | 9 |
|
9 | | - # Build a new menu item |
| 10 | + # Builds a new menu item |
10 | 11 | # |
11 | 12 | # @param [Hash] options The options for the menu |
12 | 13 | # |
13 | | - # @option options [String, Proc] :label |
14 | | - # The label to display for this menu item. It can either be a String or a |
15 | | - # Proc. If the option is Proc, it is called each time the label is requested. |
| 14 | + # @option options [String, Symbol, Proc] :label |
| 15 | + # The label to display for this menu item. |
| 16 | + # Default: Titleized Resource Name |
16 | 17 | # |
17 | 18 | # @option options [String] :id |
18 | | - # A custom id to reference this menu item with. If empty an id is automatically |
19 | | - # generated for you. |
| 19 | + # A custom id to reference this menu item with. |
| 20 | + # Default: underscored_resource_name |
20 | 21 | # |
21 | | - # @option options [String, Symbol] :url |
22 | | - # A string or symbol representing the url for this item. If it's a symbol, the |
23 | | - # view will automatically call the method for you. |
| 22 | + # @option options [String, Symbol, Proc] :url |
| 23 | + # The URL this item will link to. |
24 | 24 | # |
25 | 25 | # @option options [Integer] :priority |
26 | | - # MenuItems are sorted by priority then by label. The lower the priority, the |
27 | | - # earlier in the menu the item will be displayed. |
| 26 | + # The lower the priority, the earlier in the menu the item will be displayed. |
28 | 27 | # Default: 10 |
29 | 28 | # |
30 | | - # @option options [Proc] :if |
31 | | - # A block for the view to call to decide if this menu item should be displayed. |
32 | | - # The block should return true of false |
| 29 | + # @option options [Symbol, Proc] :if |
| 30 | + # This decides whether the menu item will be displayed. Evaluated on each request. |
33 | 31 | # |
34 | 32 | # @option options [Hash] :html_options |
35 | 33 | # A hash of options to pass to `link_to` when rendering the item |
36 | 34 | # |
37 | | - # @param [ActiveAdmin::MenuItem] parent The parent menu item of this item |
| 35 | + # @option [ActiveAdmin::MenuItem] :parent |
| 36 | + # This menu item's parent. It will be displayed nested below its parent. |
38 | 37 | # |
39 | | - def initialize(options = {}, parent = nil) |
40 | | - @label = options[:label] |
41 | | - @id = normalize_id(options[:id] || label) |
42 | | - @url = options[:url] || "#" |
43 | | - @priority = options[:priority] || 10 |
44 | | - @html_options = options[:html_options] || {} |
45 | | - @parent = parent |
46 | | - |
47 | | - @display_if_block = options[:if] |
| 38 | + # NOTE: for :label, :url, and :if |
| 39 | + # These options are evaluated in the view context at render time. Symbols are called |
| 40 | + # as methods on `self`, and Procs are exec'd within `self`. |
| 41 | + # Here are some examples of what you can do: |
| 42 | + # |
| 43 | + # menu if: :admin? |
| 44 | + # menu url: :new_book_path |
| 45 | + # menu url: :awesome_helper_you_defined |
| 46 | + # menu label: ->{ User.some_method } |
| 47 | + # menu label: ->{ I18n.t 'menus.user' } |
| 48 | + # |
| 49 | + def initialize(options = {}) |
| 50 | + super() # MenuNode |
| 51 | + @label = options[:label] |
| 52 | + @dirty_id = options[:id] || options[:label] |
| 53 | + @url = options[:url] || '#' |
| 54 | + @priority = options[:priority] || 10 |
| 55 | + @html_options = options[:html_options] || {} |
| 56 | + @should_display = options[:if] || proc{true} |
| 57 | + @parent = options[:parent] |
48 | 58 |
|
49 | 59 | yield(self) if block_given? # Builder style syntax |
50 | 60 | end |
51 | 61 |
|
52 | | - def parent? |
53 | | - !parent.nil? |
| 62 | + def id |
| 63 | + @id ||= normalize_id @dirty_id |
54 | 64 | end |
55 | 65 |
|
56 | | - def dom_id |
57 | | - case id |
58 | | - when Proc |
59 | | - id |
60 | | - else |
61 | | - id.to_s.gsub( " ", '_' ).gsub( /[^a-z0-9_]/, '' ) |
62 | | - end |
| 66 | + def label(context = nil) |
| 67 | + render_in_context context, @label |
63 | 68 | end |
64 | 69 |
|
65 | | - # Returns an array of the ancestory of this menu item |
66 | | - # The first item is the immediate parent fo the item |
67 | | - def ancestors |
68 | | - return [] unless parent? |
69 | | - [parent, parent.ancestors].flatten |
| 70 | + def url(context = nil) |
| 71 | + render_in_context context, @url |
70 | 72 | end |
71 | 73 |
|
72 | | - def <=>(other) |
73 | | - result = priority <=> other.priority |
74 | | - result = label.to_s <=> other.label.to_s if result == 0 |
75 | | - result |
| 74 | + # Don't display if the :if option passed says so |
| 75 | + # Don't display if the link isn't real, we have children, and none of the children are being displayed. |
| 76 | + def display?(context = nil) |
| 77 | + return false unless render_in_context(context, @should_display) |
| 78 | + return false if !real_url?(context) && @children.any? && !items(context).any? |
| 79 | + true |
76 | 80 | end |
77 | 81 |
|
78 | | - # Returns the display if block. If the block was not explicitly defined |
79 | | - # a default block always returning true will be returned. |
80 | | - def display_if_block |
81 | | - @display_if_block || lambda { |_| true } |
| 82 | + # Returns an array of the ancestory of this menu item. |
| 83 | + # The first item is the immediate parent of the item. |
| 84 | + def ancestors |
| 85 | + parent ? [parent, parent.ancestors].flatten : [] |
| 86 | + end |
| 87 | + |
| 88 | + private |
| 89 | + |
| 90 | + # URL is not nil, empty, or '#' |
| 91 | + def real_url?(context = nil) |
| 92 | + url = url context |
| 93 | + url.present? && url != '#' |
82 | 94 | end |
83 | 95 |
|
84 | 96 | end |
|
0 commit comments