Instantly share code, notes, and snippets.
Created
October 2, 2014 19:10
-
Star
3
(3)
You must be signed in to star a gist -
Fork
1
(1)
You must be signed in to fork a gist
-
Save npflood/2d3f5fc44518ef231195 to your computer and use it in GitHub Desktop.
Refinery Menu Presenter For Bootstrap 3.1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'active_support/core_ext/string' | |
require 'active_support/configurable' | |
require 'action_view/helpers/tag_helper' | |
require 'action_view/helpers/url_helper' | |
module Refinery | |
module Pages | |
class MenuPresenter | |
include ActionView::Helpers::TagHelper | |
include ActionView::Helpers::UrlHelper | |
include ActiveSupport::Configurable | |
config_accessor :roots, :menu_tag, :list_tag, :list_item_tag, :css, :dom_id, | |
:max_depth, :selected_css, :first_css, :last_css, :list_tag_css, | |
:link_tag_css | |
self.dom_id = 'menu' | |
self.css = "collapse navbar-collapse" | |
self.menu_tag = :div | |
self.list_tag = :ul | |
self.list_item_tag = :li | |
self.selected_css = 'active' | |
self.first_css = :first | |
self.last_css = :last | |
self.list_tag_css = "nav navbar-nav" | |
def roots | |
config.roots.presence || collection.roots | |
end | |
attr_accessor :context, :collection | |
delegate :output_buffer, :output_buffer=, :to => :context | |
def initialize(collection, context) | |
@collection = collection | |
@context = context | |
end | |
def to_html | |
render_menu(roots) if roots.present? | |
end | |
private | |
def render_menu(items) | |
content_tag(menu_tag, :id => dom_id, :class => css) do | |
render_menu_items(items) | |
end | |
end | |
def render_menu_items(menu_items) | |
if menu_items.present? | |
content_tag(list_tag, :class => list_tag_css) do | |
menu_items.each_with_index.inject(ActiveSupport::SafeBuffer.new) do |buffer, (item, index)| | |
buffer << render_menu_item(item, index) | |
end | |
end | |
end | |
end | |
def render_menu_items_children(menu_items) | |
if menu_items.present? | |
content_tag(list_tag, :class => 'dropdown-menu') do | |
menu_items.each_with_index.inject(ActiveSupport::SafeBuffer.new) do |buffer, (item, index)| | |
buffer << render_menu_item(item, index) | |
end | |
end | |
end | |
end | |
def render_menu_item_link_dropdown(menu_item) | |
link_to( menu_item.title, context.refinery.url_for(menu_item.url), class: "dropdown-toggle", data: {toggle:"dropdown", target: "#"}) | |
end | |
def render_menu_item_link(menu_item) | |
link_to(menu_item.title, context.refinery.url_for(menu_item.url), :class => link_tag_css) | |
end | |
def render_menu_item(menu_item, index) | |
content_tag(list_item_tag, :class => menu_item_css(menu_item, index)) do | |
buffer = ActiveSupport::SafeBuffer.new | |
# Check for sub menu | |
menu_item_children(menu_item).empty? ? buffer << render_menu_item_link(menu_item) : buffer << render_menu_item_link_dropdown(menu_item) | |
buffer << render_menu_items_children(menu_item_children(menu_item)) | |
buffer | |
end | |
end | |
# Determines whether any item underneath the supplied item is the current item according to rails. | |
# Just calls selected_item? for each descendant of the supplied item | |
# unless it first quickly determines that there are no descendants. | |
def descendant_item_selected?(item) | |
item.has_children? && item.descendants.any?(&method(:selected_item?)) | |
end | |
def selected_item_or_descendant_item_selected?(item) | |
selected_item?(item) || descendant_item_selected?(item) | |
end | |
# Determine whether the supplied item is the currently open item according to Refinery. | |
def selected_item?(item) | |
path = context.request.path | |
path = path.force_encoding('utf-8') if path.respond_to?(:force_encoding) | |
# Ensure we match the path without the locale, if present. | |
if %r{^/#{::I18n.locale}/} === path | |
path = path.split(%r{^/#{::I18n.locale}}).last.presence || "/" | |
end | |
# First try to match against a "menu match" value, if available. | |
return true if item.try(:menu_match).present? && path =~ Regexp.new(item.menu_match) | |
# Find the first url that is a string. | |
url = [item.url] | |
url << ['', item.url[:path]].compact.flatten.join('/') if item.url.respond_to?(:keys) | |
url = url.last.match(%r{^/#{::I18n.locale.to_s}(/.*)}) ? $1 : url.detect{|u| u.is_a?(String)} | |
# Now use all possible vectors to try to find a valid match | |
[path, URI.decode(path)].include?(url) || path == "/#{item.original_id}" | |
end | |
def menu_item_css(menu_item, index) | |
css = [] | |
css << selected_css if selected_item_or_descendant_item_selected?(menu_item) | |
css << "dropdown" unless menu_item_children(menu_item).empty? | |
css << first_css if index == 0 | |
css << last_css if index == menu_item.shown_siblings.length | |
css.reject(&:blank?).presence | |
end | |
def menu_item_children(menu_item) | |
within_max_depth?(menu_item) ? menu_item.children : [] | |
end | |
def within_max_depth?(menu_item) | |
!max_depth || menu_item.depth < max_depth | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment