techiferous / tabulous

Easy tabbed navigation for Rails
Other
322 stars 35 forks source link

in file app/tabs/tabulous.rb, method in_action only works for the active_when lines #28

Closed leisti closed 11 years ago

leisti commented 11 years ago

I'm writing a custom renderer for the tabs, since I prefer having the subtabs listed below the applicable main tab. (The default Bootstrap renderer instead treats subtabs as a dropdown menu.)

I have a separate controller for each subtab. A subtab should be active when the application is in one of the actions of that subtab's controller, and this is easy enough to specify in tabulous.rb like this:

  active_when { in_action('any').of_controller('foo_aaa') }

where foo is the name of the tab, and aaa is the name of the subtab. (Yes, I know I could have nested the controllers, so that the definition of the controller would instead be foo/aaa, but I prefer to do it this way.)

However, all these particular subtabs should be visible and enabled whenever any of the actions of any of the subcontrollers of the foo tab are active. so, I tried these definitions in tabulous.rb:

visible_when { in_action('any').of_controller('foo_aaa') or in_action('any').of_controller('foo_bbb') or in_action('any').of_controller('foo_ccc') }
enabled_when { in_action('any').of_controller('foo_aaa') or in_action('any').of_controller('foo_bbb') or in_action('any').of_controller('foo_ccc') }

where bbb and ccc are the other two subtabs. But this causes the browser to display this:

NoMethodError in Foo_aaa#show

Showing [project name elided]/app/views/layouts/application.html.erb where line #87 raised:

undefined method `in_action' for #<#<Class:0x0000000340c6a0>:0x000000034094f0>

...

app/tabs/tabulous.rb:47:in `block (4 levels) in <top (required)>'

app/views/layouts/application.html.erb:87:in `_app_views_layouts_application_html_erb__3492662668982668186_5656980'

where line 47 is the location of the visible_when line defined above.

I don't know, maybe there's a smarter way of approaching the whole situation, such as having only one controller for all the subtabs of one tab, but still, I think are situations where it would be useful to have the in_action method available for use in the visible_when and enabled_when definitions.

techiferous commented 11 years ago

Thanks for using tabulous! I think tabulous should support what you're trying to do. I need to get a better handle on what you're trying to accomplish, though. When you say:

"since I prefer having the subtabs listed below the applicable main tab."

Do you mean that you prefer the subtabs to appear visually below the containing parent tab or that you prefer the subtab markup to be nested inside the containing parent tab markup? Or something else?

leisti commented 11 years ago

I mean I want the subtabs to appear visually below the containing parent tab. This was the way the first version of Tabulous worked, and while the Bootstrap renderer that comes with the current version is quite good, I feel that for my purposes, this other way would be better.

The current Bootstrap renderer produces HTML code that contains all the subtab titles, since they're available in the drop-down menus of the respective tabs. I was afraid that I might have to use the in_action method to make sure I don't get other tabs' subtabs when I've selected a certain tab. It seems I needn't have worried; Tabulous automatically handles the situation.

Anyway, I wrote my own renderer for the situation, reproduced below. In case you think it's worth it, feel free to include this, or whatever you want to make of it, as one of the renderers of the Tabulous gem.

I'm using the active_tab_clickable false setting, and we need the anchor tags around a non-active tag so that they'll be rendered correctly by Bootstrap.

# We define our own renderer based on SplitRenderer because (a) we want
# the tabs and subtabs to be displayed separately, instead of the way
# the Bootstrap renderer provided with Tabulous does it: it displays
# subtabs as dropdown menus hanging from the relevant top-level tabs,
# and (b) we still want the produced HTML to work with Bootstrap, so we
# can't use the other Tabulous-provided renderers.
#
class SplitBootstrapRenderer < Tabulous::SplitRenderer

  # This function overrides that of DefaultRenderer so we can get the
  # "nav nav-tabs" CSS classes into the returned HTML code, so Bootstrap
  # will know how to render the tabs and subtabs.
  def tabs_html
    %Q{<div class="tabs"><ul class="nav nav-tabs">#{ tab_list_html }</ul></div>}
  end

  # This function overrides that of DefaultRenderer so we can get the
  # "nav nav-pills" CSS classes into the returned HTML code, so Bootstrap
  # will know how to render the tabs and subtabs.
  def subtabs_html
    %Q{<div class="subtabs"><ul class="nav nav-pills nav-subtabs">#{ subtab_list_html }</ul></div>}
  end

  protected

  def tab_html(tab)
    return '' unless tab.visible?(@view)
    html = ''
    klass = ''
    klass << 'active' if tab.active?(@view)
    klass << ' disabled' unless tab.enabled?(@view)
    klass.strip!
    if klass.empty?
      html << '<li>'
    else
      html << %Q{<li class="#{klass}">}
    end
    if tab.clickable?(@view) && tab.enabled?(@view)
      html << %Q{<a href="#{tab.link_path(@view)}">#{tab.text(@view)}</a>}
    else
      html << %Q{<a>#{tab.text(@view)}</a>}
    end
    html << "</li>"
    html
  end

end

As you see, I'm using nav-pills for the subtab level, because I thought it looked better. Here's the CSS code I use that overrides the default Boostrap CSS. "nav-subtabs" is a CSS class of my own invention.

@import "bootstrap";

...

$fontsize_large:     16px;
$fontsize_standard:  14px;
$fontsize_small:     12px;

...

$almost_white:  #eeeeee;
$dark_grey:     #444444;
$light_green:   #bcebb5;
$medium_grey:   #999999;
$dark_green:    #306819;

...

/* Colors, borders, and font size for tabs. */
.nav-tabs {
  border-bottom: 1px solid $medium_grey;
  margin-bottom: 0px;
  li > a {
    /* Use a large font to emphasize the importance of this navigation element. */
    font-size: $fontsize_large;
    color: $dark_grey;
  }
  li > a:focus {
    color: $black;
    background-color: $light_green;
  }
  li > a:hover {
    color: $black;
    background-color: $light_green;
    border-color: $almost_white $almost_white $medium_grey;
  }
  .active > a,
  .active > a:hover {
    color: $dark_green;
    font-weight: bold;
    border: 1px solid $medium_grey;
    border-bottom-color: transparent;
  }
}

/* Colors and font size for subtabs. */
.nav-subtabs {
  border-bottom: 1px solid $medium_grey;
  li > a {
    /* Use the standard font size, so the subtabs' text won't be as large that of tabs. */
    font-size: $fontsize_standard;
    color: $dark_grey;
    padding-top: 6px;
    padding-bottom: 0px;
    margin-top: 3px;
    margin-bottom: 3px;
    height: $fontsize_standard + 6px;
  }
  li > a:focus,
  li > a:hover {
    color: $black;
    background-color: $light_green;
  }
  .active > a,
  .active > a:hover {
    background-color: $white;
    color: $dark_green;
    font-weight: bold;
  }
}
techiferous commented 11 years ago

Ah, okay. I see what's going on now. I actually didn't realize that I programmed tabulous 2 in a way that produced a different tab layout with Bootstrap! Thanks for letting me know and for including code, which provides a good example for someone else in that situation.

It looks like you were able to figure this out. Are you all set now? Also, did you find it easy to figure out how to write your own renderer or was the documentation too sparse?

leisti commented 11 years ago

You're welcome. Yes, I'm all set now, thanks, so this issue can be closed. In order to write the new renderer, I did need to look at the code of the renderers included in the gem. It's understandable once you figure out how the existing code works, but perhaps a few more lines of documentation would not go amiss.

techiferous commented 11 years ago

Great. Thanks for the feedback, and I'm glad you're all set.