bettertabs 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +14 -0
- data/README.md +288 -0
- data/Rakefile +2 -0
- data/bettertabs.gemspec +19 -0
- data/lib/bettertabs.rb +7 -0
- data/lib/bettertabs/bettertabs_builder.rb +162 -0
- data/lib/bettertabs/bettertabs_helper.rb +16 -0
- data/lib/bettertabs/javascripts/README.md +38 -0
- data/lib/bettertabs/javascripts/jquery.bettertabs.coffee +77 -0
- data/lib/bettertabs/javascripts/jquery.bettertabs.js +81 -0
- data/lib/bettertabs/javascripts/jquery.bettertabs.min.js +4 -0
- data/lib/bettertabs/version.rb +3 -0
- data/test/README_for_TEST.txt +5 -0
- data/test/ruby_1_9/rails_3_0/.rspec +1 -0
- data/test/ruby_1_9/rails_3_0/.rvmc +1 -0
- data/test/ruby_1_9/rails_3_0/Gemfile +11 -0
- data/test/ruby_1_9/rails_3_0/Gemfile.lock +105 -0
- data/test/ruby_1_9/rails_3_0/README +3 -0
- data/test/ruby_1_9/rails_3_0/Rakefile +7 -0
- data/test/ruby_1_9/rails_3_0/app/controllers/application_controller.rb +3 -0
- data/test/ruby_1_9/rails_3_0/app/controllers/bettertabs_controller.rb +24 -0
- data/test/ruby_1_9/rails_3_0/app/helpers/application_helper.rb +2 -0
- data/test/ruby_1_9/rails_3_0/app/views/bettertabs/_ajax.html.haml +6 -0
- data/test/ruby_1_9/rails_3_0/app/views/bettertabs/_link.html.haml +4 -0
- data/test/ruby_1_9/rails_3_0/app/views/bettertabs/_mixed.html.haml +6 -0
- data/test/ruby_1_9/rails_3_0/app/views/bettertabs/_tab_content.html.haml +1 -0
- data/test/ruby_1_9/rails_3_0/app/views/bettertabs/ajax.html.haml +2 -0
- data/test/ruby_1_9/rails_3_0/app/views/bettertabs/link_tab_1.html.haml +2 -0
- data/test/ruby_1_9/rails_3_0/app/views/bettertabs/link_tab_2.html.haml +2 -0
- data/test/ruby_1_9/rails_3_0/app/views/bettertabs/mixed.html.haml +2 -0
- data/test/ruby_1_9/rails_3_0/app/views/bettertabs/mixed_with_erb.html.erb +8 -0
- data/test/ruby_1_9/rails_3_0/app/views/bettertabs/static.html.haml +7 -0
- data/test/ruby_1_9/rails_3_0/app/views/layouts/application.html.erb +23 -0
- data/test/ruby_1_9/rails_3_0/config.ru +4 -0
- data/test/ruby_1_9/rails_3_0/config/application.rb +47 -0
- data/test/ruby_1_9/rails_3_0/config/boot.rb +6 -0
- data/test/ruby_1_9/rails_3_0/config/environment.rb +5 -0
- data/test/ruby_1_9/rails_3_0/config/environments/development.rb +26 -0
- data/test/ruby_1_9/rails_3_0/config/environments/test.rb +35 -0
- data/test/ruby_1_9/rails_3_0/config/initializers/mime_types.rb +5 -0
- data/test/ruby_1_9/rails_3_0/config/initializers/secret_token.rb +7 -0
- data/test/ruby_1_9/rails_3_0/config/initializers/session_store.rb +8 -0
- data/test/ruby_1_9/rails_3_0/config/routes.rb +6 -0
- data/test/ruby_1_9/rails_3_0/public/404.html +26 -0
- data/test/ruby_1_9/rails_3_0/public/422.html +26 -0
- data/test/ruby_1_9/rails_3_0/public/500.html +26 -0
- data/test/ruby_1_9/rails_3_0/public/favicon.ico +0 -0
- data/test/ruby_1_9/rails_3_0/public/images/rails.png +0 -0
- data/test/ruby_1_9/rails_3_0/public/javascripts/jquery-1.5.2.min.js +16 -0
- data/test/ruby_1_9/rails_3_0/public/javascripts/jquery.bettertabs.min.js +4 -0
- data/test/ruby_1_9/rails_3_0/public/javascripts/rails.js +278 -0
- data/test/ruby_1_9/rails_3_0/public/robots.txt +5 -0
- data/test/ruby_1_9/rails_3_0/public/stylesheets/bettertabs.css +23 -0
- data/test/ruby_1_9/rails_3_0/public/stylesheets/layout.css +8 -0
- data/test/ruby_1_9/rails_3_0/script/rails +6 -0
- data/test/ruby_1_9/rails_3_0/spec/requests/bettertabs_spec.rb +54 -0
- data/test/ruby_1_9/rails_3_0/spec/spec_helper.rb +19 -0
- metadata +122 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/README.md
ADDED
@@ -0,0 +1,288 @@
|
|
1
|
+
Bettertabs
|
2
|
+
==========
|
3
|
+
|
4
|
+
We know that splitting content into several tabs is easy, but doing well, clean, DRY, accessible, usable, fast and testable is not so simple after all.
|
5
|
+
|
6
|
+
Bettertabs is a helper for Rails that renders the markup for a tabbed area in a easy and declarative way, forcing you to keep things simple and ensuring accessibility and usability, no matter if the content is loaded statically or via ajax.
|
7
|
+
|
8
|
+
|
9
|
+
## Features and Key Points ##
|
10
|
+
|
11
|
+
* Generate markup with a rails helper and add JavaScript behavior with a jQuery plugin
|
12
|
+
* Simplicity: Easy to install and easy to use
|
13
|
+
* Forces you to DRY-up your views for your tabbed content
|
14
|
+
* Forces you to make it awesome accessible and usable:
|
15
|
+
* Designed to work with and without JavaScript
|
16
|
+
* When click on a tab, the address bar url is changed (feature supported only in HTML5 browsers), so:
|
17
|
+
* The browser's back and reload buttons will still work as expected
|
18
|
+
* All tabbed sections can be permalinked, keeping the selected tab
|
19
|
+
* Flexible and customizable
|
20
|
+
* Makes testing views simple. Because it works without javascript, you can assert view components with the rails built-in functional and integration tests.
|
21
|
+
* The CSS styles are up to you.
|
22
|
+
|
23
|
+
|
24
|
+
## Requirements: ##
|
25
|
+
* Ruby 1.9.2
|
26
|
+
* Rails 3
|
27
|
+
* The [Bettertabs jQuery plugin](https://github.com/agoragames/bettertabs/raw/master/lib/bettertabs/javascripts/jquery.bettertabs.min.js) (that requires [jQuery](http://jquery.com/) 1.3, 1.4 or 1.5)
|
28
|
+
|
29
|
+
Although you can use bettertabs without javascript, and also should be possible to use another javascript for this, since the bettertabs helper only generates the appropiate markup.
|
30
|
+
|
31
|
+
|
32
|
+
## Install ##
|
33
|
+
|
34
|
+
Gem dependency. Add bettertabs to your gem file and run `bundle install`.
|
35
|
+
|
36
|
+
gem 'bettertabs'
|
37
|
+
|
38
|
+
|
39
|
+
Download the [bettertabs.jquery.min plugin](https://github.com/agoragames/bettertabs/raw/master/lib/bettertabs/javascripts/jquery.bettertabs.min.js) (or the [uncompressed version](https://github.com/agoragames/bettertabs/raw/master/lib/bettertabs/javascripts/jquery.bettertabs.js)), put it in your `public/javascripts` folder and include it in your layout after the jQuery library, for example:
|
40
|
+
|
41
|
+
<%= javascript_include_tag 'jquery', 'rails', 'jquery.bettertabs.min' %>
|
42
|
+
|
43
|
+
Now you can use the `bettertabs` helper as in the examples below.
|
44
|
+
|
45
|
+
|
46
|
+
## Usage and examples ##
|
47
|
+
|
48
|
+
Bettertabs supports three kinds of tabs:
|
49
|
+
|
50
|
+
* **Link Tabs**: Loads only the active tab contents; when click on another tab, go to the specified URL. No JavaScript needed.
|
51
|
+
* **Static Tabs**: Loads all content of all static tabs, but only show the active content; when click on another tab, activate its related content. When JavaScript disabled, it behaves like *link tabs*.
|
52
|
+
* **Ajax Tabs**: Loads only the active tab contents; when click on another tab, loads its content via ajax and show. When JavaScript disabled, it behaves like *link tabs*.
|
53
|
+
|
54
|
+
|
55
|
+
### Link Tabs Example ###
|
56
|
+
|
57
|
+
A simple tabbed content can be created using linked tabs:
|
58
|
+
|
59
|
+
In view `app/views/home/simple.html.erb`:
|
60
|
+
|
61
|
+
<%= bettertabs :simpletabs do |tab| %>
|
62
|
+
<%= tab.link :tab1, 'Tab One' do %>
|
63
|
+
Hello world.
|
64
|
+
<% end %>
|
65
|
+
<%= tab.link :tab2, 'Tab Two' do %>
|
66
|
+
This is the <b>dark side</b> of the moon.
|
67
|
+
<% end %>
|
68
|
+
<% end %>
|
69
|
+
|
70
|
+
|
71
|
+
This will define a linked two-tabs widget. The second tab ("Tab Two") is a link to the same url, but with the extra param `?simpletabs_selected_tab=tab2`, that will make the bettertabs helper activate the :tab2 tab.
|
72
|
+
|
73
|
+
In this case no JavaScript is required, and a new request will be performed when click the tabs links.
|
74
|
+
To change this to a javascript static tabs, you only need to change `tab.link` declarations for `tab.static`.
|
75
|
+
To change this to ajax tabs, in this case, is only needed to change the `tab.link` declarations for `tab.ajax`.
|
76
|
+
|
77
|
+
|
78
|
+
### Static Tabs Example ###
|
79
|
+
|
80
|
+
This example will show three tabs: "Home", "Who am I?" and "Contact Me".
|
81
|
+
The partials and files organization used in this examples are not mandatory but recommended, it will make easier to change to ajax tabs if needed.
|
82
|
+
|
83
|
+
In view `app/views/home/index.html.erb`:
|
84
|
+
|
85
|
+
<h1>Static Tabs Example</h1>
|
86
|
+
<%= render 'mytabs' %>
|
87
|
+
|
88
|
+
In partial `app/views/home/_mytabs.html.erb`:
|
89
|
+
|
90
|
+
<%= bettertabs :mytabs do |tab| %>
|
91
|
+
<%= tab.static :home do %>
|
92
|
+
<h2>Home</h2>
|
93
|
+
<%= raw @content_for_home %>
|
94
|
+
<% end %>
|
95
|
+
<%= tab.static :about, 'Who am I?', :partial => '/shared/about' %>
|
96
|
+
<%= tab.static :contact_me %>
|
97
|
+
<% end %>
|
98
|
+
|
99
|
+
|
100
|
+
The :about and :contact_me tabs will get the content from the referenced partials. Put any content there, for example:
|
101
|
+
|
102
|
+
In partial `app/views/shared/_about.html.erb`:
|
103
|
+
|
104
|
+
Hello, my name is <%= @myname %>.
|
105
|
+
|
106
|
+
In partial `app/views/home/_contact_me.html.erb`:
|
107
|
+
|
108
|
+
<h2>How to contact me:<h2/>
|
109
|
+
<p><%= @contact_info.inspect %></p>
|
110
|
+
|
111
|
+
In controller `app/controllers/home_controller.rb`:
|
112
|
+
|
113
|
+
class HomeController < ApplicationController
|
114
|
+
|
115
|
+
def index
|
116
|
+
# Declare instance variables as usual
|
117
|
+
@content_for_home = very_slow_function()
|
118
|
+
@myname = 'Lucifer'
|
119
|
+
@contact_info = { :address => 'The Hell', :telephone => '666'}
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
This will work with and without JavaScript, anyway, all vars and content should be loaded in the request because all static tabs need to render its content.
|
125
|
+
|
126
|
+
|
127
|
+
### Improve with AJAX ###
|
128
|
+
|
129
|
+
Ajax tabs call the same URL as link tabs but loading the content asynchronously.
|
130
|
+
|
131
|
+
In the previous example, `@content_for_home = very_slow_function()` has to be executed even if the user see another tab content. Using ajax or link tabs, the content is only loaded when needed.
|
132
|
+
|
133
|
+
Lets create some tabs with ajax:
|
134
|
+
|
135
|
+
In view `app/views/home/index.html.erb`:
|
136
|
+
|
137
|
+
<h1>Ajax Tabs Example</h1>
|
138
|
+
<%= render 'mytabs' %>
|
139
|
+
|
140
|
+
In partial `app/views/home/_mytabs.html.erb`:
|
141
|
+
|
142
|
+
<%= bettertabs :mytabs do |tab| %>
|
143
|
+
<%= tab.ajax :home do %>
|
144
|
+
<h2>Home</h2>
|
145
|
+
<%= raw @content_for_home %>
|
146
|
+
<% end %>
|
147
|
+
<%= tab.ajax :about, 'Who am I?', :partial => '/shared/about' %>
|
148
|
+
<%= tab.ajax :contact_me %>
|
149
|
+
<% end %>
|
150
|
+
|
151
|
+
**Note** that the only difference between this example and the *static tabs example* is to use `tab.ajax` declaration instead of `tab.static`.
|
152
|
+
|
153
|
+
Partials `app/views/shared/_about.html.erb` and `app/views/home/_contact_us.html.erb` (same as the static tabs example).
|
154
|
+
|
155
|
+
In controller `app/controllers/home_controller.rb`, you can load only the needed data for each tab:
|
156
|
+
|
157
|
+
class HomeController < ApplicationController
|
158
|
+
|
159
|
+
def index
|
160
|
+
# Execute only the selected tab needed code (optimization).
|
161
|
+
case params[:mytabs_selected_tab]
|
162
|
+
when 'home' then
|
163
|
+
@content_for_home = very_slow_function()
|
164
|
+
when 'about' then
|
165
|
+
@myname = 'Lucifer'
|
166
|
+
when 'contact_us' then
|
167
|
+
@contact_info = { :address => 'The Hell', :telephone => '666'}
|
168
|
+
end
|
169
|
+
|
170
|
+
# When ajax, load only the selected tab content (handled by bettertabs helper)
|
171
|
+
respond_to do |format|
|
172
|
+
format.js { render :partial => 'mytabs' } # when ajax call, bettertabs only renders the active content.
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
The only needed code in the controller to make it work with ajax is the `format.js { render :partial => 'mytabs' }` line.
|
179
|
+
|
180
|
+
Since the *_mytabs* partial only contains the bettertabs helper, and bettertabs helper only renders the selected content when ajax call, this line is enough to return the selected content to the ajax call.
|
181
|
+
|
182
|
+
#### Advantages if you follow this file structure ####
|
183
|
+
|
184
|
+
Having the bettertabs helper in a separated partial gives you the following advantages:
|
185
|
+
|
186
|
+
* It works without JavaScript out-of-the-box
|
187
|
+
* Since the URL is changed in the browser when a tab is clicked and it works without JavaScript, a permalink is defined for each tab, so it allows:
|
188
|
+
* to bookmark the page on any selected tab
|
189
|
+
* to reload the page keeping the selected tab
|
190
|
+
* Easily change the behavior of a tab to be `ajax`, `static` or `link`. It always work.
|
191
|
+
* Keep your views DRY, clean and readable
|
192
|
+
|
193
|
+
|
194
|
+
### Mixed Example ###
|
195
|
+
|
196
|
+
Is easy to mix all types of tabs, and customize them using the provided options. For example (Using ruby 1.9.2 with HAML):
|
197
|
+
|
198
|
+
= bettertabs :bettertabs_example, :selected_tab => :chooseme do |tab|
|
199
|
+
= tab.static :simplest_tab, class: 'awesome-tab' do
|
200
|
+
Click this tab to see this content.
|
201
|
+
|
202
|
+
= tab.static :chooseme, 'Please, Click me!' # as default, renders partial: 'chooseme'
|
203
|
+
|
204
|
+
= tab.static :render_another_partial, partial: 'another_partial'
|
205
|
+
|
206
|
+
= tab.link :link_to_another_place, url: go_to_other_place_url # will make a new request
|
207
|
+
|
208
|
+
= tab.ajax :simple_ajax, title: 'Content is loaded when needed and only once.'
|
209
|
+
|
210
|
+
= tab.ajax :album, url: show_remote_album_path, partial: 'shared/album'
|
211
|
+
|
212
|
+
|
213
|
+
#### From the Controller: ####
|
214
|
+
|
215
|
+
The default tab :url option value is the current path but with `param[:"{bettertabs_id}_selected_tab"]` to the current tab id.
|
216
|
+
This means that, by default, you can handle all needed information in the same controller action for all tab contents. Of course you can change this using a different value for the :url option.
|
217
|
+
|
218
|
+
If the tab is ajax, the ajax call will be performed to the same url provided in the :url option.
|
219
|
+
You can know if a call is ajax checking if `controller.request.xhr?` is true.
|
220
|
+
|
221
|
+
So, if `request.xhr?` is *true* then you should render a partial to be used as content.
|
222
|
+
|
223
|
+
If `request.xhr?` is *false* then you can just render the action as usual, bettertabs helper will auto select the appropriate tab based on `params[:"{bettertabs_id}_selected_tab"]` value.
|
224
|
+
|
225
|
+
When a call is AJAX, the bettertabs helper only render the active content (no tabs, no wrappers, no other content), so you can just render the partial where the bettertabs helper is used and it will work for any selected tab.
|
226
|
+
|
227
|
+
You can write your controller code as follows, assuming that bettertabs_id is :example, and it's placed alone in the partial 'bettertabs_example':
|
228
|
+
|
229
|
+
def show_bettertabs_example
|
230
|
+
@common_for_all_tabs = code_here
|
231
|
+
|
232
|
+
# A simple way of execute code only for the selected tab.
|
233
|
+
# Note that static tabs are always rendered, even if they are not selected.
|
234
|
+
case params[:example_selected_tab]
|
235
|
+
when 'chooseme' then
|
236
|
+
code_only_for_this_tab
|
237
|
+
else
|
238
|
+
code_for_all_other_tabs
|
239
|
+
end
|
240
|
+
|
241
|
+
# Ready for ajax ...
|
242
|
+
# You can check request.xhr? or use the common method respond_to
|
243
|
+
respond_to do |format|
|
244
|
+
format.js { render :partial => 'bettertabs_example' } # render only the selected content
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
|
250
|
+
## TODO ##
|
251
|
+
|
252
|
+
* Improve the Rails testing application.
|
253
|
+
* it should use rspec and capybabra to test even the javascript (http://media.railscasts.com/videos/257_request_specs_and_capybara.mov)
|
254
|
+
* Try to make it work with ruby 1.8.x
|
255
|
+
* Create a basic CSS stylesheet that may serve as base (and add the links here in the documentation)
|
256
|
+
|
257
|
+
|
258
|
+
## Current Spec sheet (will become automatic tests) ##
|
259
|
+
* Should always work with javascript disabled (using the urls of the tabs links)
|
260
|
+
* The bettertabs hepler should accept:
|
261
|
+
* args: (bettertabs_id, options)
|
262
|
+
* options can be:
|
263
|
+
* :selected_tab => tab_id to select by default
|
264
|
+
* :selected_tab should be overridden with params[:"#{bettertabs_id}_selected_tab"] if present
|
265
|
+
* any other option is used as wrapper html_options (wrapper is the top-level widget dom element).
|
266
|
+
* The bettertabs helper should render clear markup:
|
267
|
+
* A wrapper with class 'bettertabs'
|
268
|
+
* Tabs markup
|
269
|
+
* ul.tabs > li > a
|
270
|
+
* selected tab is ul.tabs > li.active > a
|
271
|
+
* each a element has element attributes:
|
272
|
+
* data-tab-type (for javascript: change click behavior depending on type "static", "link" or "ajax")
|
273
|
+
* data-show-content-id (for javascript: element id to show when select this tab)
|
274
|
+
* sections for each tab content.
|
275
|
+
* use a unique html id (based on bettertabs_id and tab_id) for each Tab and Content
|
276
|
+
* The bettertabs builder ".from" method should accept:
|
277
|
+
* args: (tab_id, tab_name, options, &block)
|
278
|
+
* args: (tab_id, options, &block)
|
279
|
+
* If block_given? the block is used as content related to this tab
|
280
|
+
* options can be:
|
281
|
+
* :partial => to use as content. Defaults to tab_id
|
282
|
+
* if block_given? this option can not be used (if used, raise an error)
|
283
|
+
* :url => for the tab link, that should go to this selected tab when javascript is disabled. Defaults to { :"#{bettertabs_id}_selected_tab" => tab_id }
|
284
|
+
* :tab_type => used in the markup as the link data-tab-type value. Can be :static, :link or :ajax (or the corresponding strings). Raise error otherwise. Defaults to :static.
|
285
|
+
* The bettertabs builder ".static", ".link" and ".ajax" methods are only a convenient way to use ".for" method with :tab_type set to :static, :link or :ajax respectively.
|
286
|
+
* Content is rendered only for active tab, except when tab_type is :static, where content is always rendered (ready to show when select that tab using javascript).
|
287
|
+
* When ajax call (format.js), the bettertabs helper should return ONLY the content of the selected tab (to simplify the controller render partial calls.).
|
288
|
+
|
data/Rakefile
ADDED
data/bettertabs.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "bettertabs/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "bettertabs"
|
7
|
+
s.version = Bettertabs::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Mario Izquierdo"]
|
10
|
+
s.email = ["tothemario@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{The better (simple, accessible, usable, flexible and fast) way to split content in tabs.}
|
13
|
+
s.description = %q{The bettertabs helper defines the markup for a tabbed area in a easy and declarative way, using the appropiate JavaScript but ensuring accessibility and usability, no matter if the content is loaded statically, via ajax or the tabs are links. In the other hand, the CSS styles are up to you. }
|
14
|
+
|
15
|
+
s.rubyforge_project = "bettertabs"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
end
|
data/lib/bettertabs.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
class BettertabsBuilder
|
2
|
+
|
3
|
+
TAB_TYPE_STATIC = :static
|
4
|
+
TAB_TYPE_LINK = :link
|
5
|
+
TAB_TYPE_AJAX = :ajax
|
6
|
+
TAB_TYPES = [TAB_TYPE_STATIC, TAB_TYPE_LINK, TAB_TYPE_AJAX]
|
7
|
+
|
8
|
+
def initialize(bettertabs_id, template, selected_tab_id = nil, options = {})
|
9
|
+
@bettertabs_id = bettertabs_id
|
10
|
+
@template = template
|
11
|
+
@selected_tab_id = selected_tab_id
|
12
|
+
@render_only_active_content = options.delete(:render_only_active_content) # used in ajax calls
|
13
|
+
@wrapper_html_options = options
|
14
|
+
|
15
|
+
@tabs = []
|
16
|
+
@contents = []
|
17
|
+
end
|
18
|
+
|
19
|
+
# Static tabs generator
|
20
|
+
def static(tab_id, *args, &block)
|
21
|
+
get_options(args)[:tab_type] = TAB_TYPE_STATIC
|
22
|
+
self.for(tab_id, *args, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Linked tabs generator
|
26
|
+
def link(tab_id, *args, &block)
|
27
|
+
get_options(args)[:tab_type] = TAB_TYPE_LINK
|
28
|
+
self.for(tab_id, *args, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Ajax tabs generator
|
32
|
+
def ajax(tab_id, *args, &block)
|
33
|
+
get_options(args)[:tab_type] = TAB_TYPE_AJAX
|
34
|
+
self.for(tab_id, *args, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Generic tab and content generator
|
38
|
+
def for(tab_id, *args, &block)
|
39
|
+
# Initialize vars and options
|
40
|
+
options = get_options(args)
|
41
|
+
tab_id = tab_id.to_s
|
42
|
+
tab_text = get_tab_text(tab_id, args)
|
43
|
+
raise "Bettertabs: #{tab_html_id_for(tab_id)} error. Used :partial option and a block of content at the same time." if block_given? and options[:partial]
|
44
|
+
partial = options.delete(:partial) || tab_id.to_s unless block_given?
|
45
|
+
url = options.delete(:url) || { :"#{@bettertabs_id}_selected_tab" => tab_id }
|
46
|
+
tab_type = (options.delete(:tab_type) || TAB_TYPE_STATIC).to_sym
|
47
|
+
raise "Bettertabs: #{tab_type.inspect} tab type not supported. Use one of #{TAB_TYPES.inspect} instead." unless TAB_TYPES.include?(tab_type)
|
48
|
+
@selected_tab_id ||= tab_id # defaults to first tab
|
49
|
+
|
50
|
+
if @render_only_active_content
|
51
|
+
if active?(tab_id)
|
52
|
+
@only_active_content = block_given? ? @template.capture(&block) : @template.render(:partial => partial)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
# Tabs
|
56
|
+
tab_html_options = options # any other option will be used as tab html_option
|
57
|
+
@tabs << { tab_id: tab_id, text: tab_text, url: url, html_options: tab_html_options, tab_type: tab_type, active: active?(tab_id) }
|
58
|
+
|
59
|
+
# Content
|
60
|
+
content_html_options = { id: content_html_id_for(tab_id), class: "content #{active?(tab_id) ? 'active' : 'hidden'}" }
|
61
|
+
if active?(tab_id) or tab_type == TAB_TYPE_STATIC # Only render content for selected tab (static content is always rendered).
|
62
|
+
content = block_given? ? @template.capture(&block) : @template.render(:partial => partial)
|
63
|
+
else
|
64
|
+
content = ''
|
65
|
+
end
|
66
|
+
@contents << { tab_id: tab_id, tab_text: tab_text, content: content, html_options: content_html_options, tab_type: tab_type, active: active?(tab_id) }
|
67
|
+
end
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
# Renders the bettertabs markup.
|
72
|
+
# The following instance variables are available:
|
73
|
+
# * @tabs: list of tabs. Each one is a hash with the followin keys:
|
74
|
+
# * :tab_id => symbol identifier of the tab
|
75
|
+
# * :text => text to show in the rendered tab
|
76
|
+
# * :url => string with the tab url
|
77
|
+
# * :html_options => for use in the tab markup (included 'data-tab-type').
|
78
|
+
# * :tab_type => One of TAB_TYPES
|
79
|
+
# * :active => true if this is the selected tab
|
80
|
+
# * @contents: list of contents asociated with each tab. Each one is a hash with the following keys:
|
81
|
+
# * :tab_id => symbol identifier of the tab
|
82
|
+
# * :tab_text => text that is used in the corresponding tab
|
83
|
+
# * :content => string with the content inside
|
84
|
+
# * :html_options => for use in the container markup
|
85
|
+
# * :tab_type => One of TAB_TYPES
|
86
|
+
# * :active => true if this is the selected content
|
87
|
+
# * @selected_tab_id: String with the selected tab id (to match against each tab[:tab_id])
|
88
|
+
# * @wrapper_html_options: hash with the html options used in the bettertabs container
|
89
|
+
# * @bettertabs_id: id of the bettertabs widget
|
90
|
+
def render
|
91
|
+
if @render_only_active_content
|
92
|
+
@only_active_content.html_safe
|
93
|
+
else
|
94
|
+
|
95
|
+
# Wrapper
|
96
|
+
@wrapper_html_options ||= {}
|
97
|
+
@wrapper_html_options[:"data-initial-active-tab-id"] = tab_html_id_for @selected_tab_id
|
98
|
+
tag(:div, @wrapper_html_options) do
|
99
|
+
|
100
|
+
# Tabs list
|
101
|
+
tag(:ul, class: 'tabs') do
|
102
|
+
@tabs.map do |tab|
|
103
|
+
tag(:li, class: ('active' if tab[:active]), id: tab_html_id_for(tab[:tab_id])) do
|
104
|
+
tab[:html_options][:"data-tab-type"] ||= tab[:tab_type] # for javascript: change click behavior depending on type :static, :link or :ajax
|
105
|
+
tab[:html_options][:"data-show-content-id"] ||= content_html_id_for(tab[:tab_id]) # for javascript: element id to show when select this tab
|
106
|
+
@template.link_to(tab[:text], tab[:url], tab[:html_options])
|
107
|
+
end
|
108
|
+
end.join.html_safe
|
109
|
+
end +
|
110
|
+
|
111
|
+
# Content sections
|
112
|
+
@contents.map do |content|
|
113
|
+
tag(:div, content[:html_options]) do
|
114
|
+
content[:content] # this should be blank unless content[:active] or content[:tab_type] == :static
|
115
|
+
end
|
116
|
+
end.join.html_safe
|
117
|
+
end.html_safe +
|
118
|
+
jquery_tag("$('##{@bettertabs_id}').bettertabs();")
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
# Alias of @template.content_tag
|
127
|
+
def tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
|
128
|
+
@template.content_tag(name, content_or_options_with_block, options, escape, &block)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Wraps javascript code inside a javascript_tag using the jQuery(function($){ ... }); on init call.
|
132
|
+
def jquery_tag(jquery_code)
|
133
|
+
@template.javascript_tag("jQuery(function($){ #{ jquery_code } });")
|
134
|
+
end
|
135
|
+
|
136
|
+
# Get the options hash from an array of args. If options are not present, create them and initialize to {}
|
137
|
+
def get_options(args)
|
138
|
+
args << {} unless args.last.is_a?(Hash)
|
139
|
+
args.last
|
140
|
+
end
|
141
|
+
|
142
|
+
# Get the default name of the tab, from the args list.
|
143
|
+
def get_tab_text(tab_id, args)
|
144
|
+
String === args.first ? args.first : tab_id.to_s.titleize
|
145
|
+
end
|
146
|
+
|
147
|
+
# Check if this tab_id is the same as @selected_tab_id
|
148
|
+
def active?(tab_id)
|
149
|
+
@selected_tab_id.to_s == tab_id.to_s
|
150
|
+
end
|
151
|
+
|
152
|
+
# Tab html id
|
153
|
+
def tab_html_id_for(tab_id)
|
154
|
+
"#{tab_id}_#{@bettertabs_id}_tab"
|
155
|
+
end
|
156
|
+
|
157
|
+
# Content html id
|
158
|
+
def content_html_id_for(tab_id)
|
159
|
+
"#{tab_id}_#{@bettertabs_id}_content"
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|