makandra-navy 0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown ADDED
@@ -0,0 +1,116 @@
1
+ Navy
2
+ ====
3
+
4
+ Navy support rendering for horizontal, multi-level navigation bars. Sections are dynamic and can depend on user permissions etc.
5
+
6
+ Some of the features are:
7
+
8
+ - A simple but powerful DSL to describe the navigation structure
9
+ - Subnavigations can be previewed by clicking an expand arrow
10
+ - Sections that don't fit into the navigation bar are automatically moved into a dropdown
11
+
12
+
13
+ How it works
14
+ -----------
15
+
16
+ The gem provides the DSL to specify the navigation structure as well as a renderer that outputs appropriate HTML.
17
+
18
+ To style it, we provide a sample .sass stylesheet you have to copy and include into your stylesheet, as well as adapt to your design.
19
+
20
+ The javascript functionality is provided in navy.js that you have to copy and include in your application.
21
+
22
+
23
+ Caveats
24
+ -------
25
+
26
+ The gem should be considered in an alpha state.
27
+
28
+ Styles are not very flexible yet, it might be hard to adapt them to your needs.
29
+
30
+ The standard styles do not work well with old browsers (e.g. IE 8).
31
+
32
+ The JavaScript features are somewhat dependent on the CSS (to determine when sections will be collapsed). In particular, you have to make sure that the dropdown arrow floats correctly right of the navigation sections.
33
+
34
+ Some assets have to be copied manually, there is no generator yet.
35
+
36
+
37
+ Installation
38
+ ------------
39
+
40
+ Put this into your Gemfile:
41
+
42
+ gem 'makandra-navy', :require => 'navy'
43
+
44
+
45
+ Copy stylesheets, javascripts and (perhaps) cucumber steps from /assets into your project.
46
+
47
+
48
+
49
+ Usage
50
+ -----
51
+
52
+ Navigation structure is defined with a class like this (somewhere in your load path):
53
+
54
+ class Navigation
55
+ include Navy::Description
56
+
57
+ navigation :main do
58
+
59
+ section :dashboard, "Dashboard", root_path
60
+
61
+ if current_user.admin?
62
+ section :admin, 'Admin' do
63
+ section :admin_users, "Users", admin_users_path
64
+
65
+ if current_user.may_admin_projects?
66
+ section :admin_projects, "Projects", admin_projects_path
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ navigation :footer do
73
+ section :terms, "Terms and Conditions", terms_and_conditions_path
74
+ section :imprint, "Imprint", imprint_path
75
+ end
76
+
77
+ navigation :user do |user|
78
+ section :settings, "Settings", user_settings_path(user)
79
+ end
80
+
81
+ end
82
+
83
+
84
+ This is all evaluated in your view context, so helpers will work.
85
+
86
+ To actually render a navigation, do this in your view:
87
+
88
+ <%= render_navigation Navigation.main, :collapse => true %>
89
+
90
+ <%= render_navigation Navigation.footer %>
91
+
92
+ <%= render_navigation Navigation.user(current_user) %>
93
+
94
+ The `:collapse => true` option enables the automatic moving of navigation sections into a dropdown if they do not fit.
95
+
96
+
97
+ Cucumber
98
+ -------
99
+
100
+ There are some cucumber steps in /assets.
101
+
102
+ The most important one is:
103
+
104
+
105
+ When I open the "Admin" section
106
+
107
+
108
+ To open submenu entries, use
109
+
110
+ When I open the "Admin > Projects" section
111
+
112
+
113
+ Credits
114
+ -------
115
+
116
+ Tobias Kraze, Henning Koch
@@ -0,0 +1,36 @@
1
+ When /^I open the "([^"]*)" (?:section|tab)$/ do |all_labels|
2
+ labels = all_labels.split('>').collect(&:squish)
3
+ if Capybara.current_session.driver.is_a? Capybara::Driver::Selenium
4
+ if labels.size == 2
5
+ When "I expand the \"#{labels.first}\" section"
6
+ end
7
+ When "I follow \"#{labels.last}\" within \".navy-level-#{labels.size}\""
8
+ else
9
+ opens = nil
10
+ section = nil
11
+ labels.each_with_index do |label, index|
12
+ scope = "div[data-navy-navigation-level='#{index + 1}']"
13
+ if opens
14
+ scope << "[data-navy-opened-by='#{opens}']"
15
+ end
16
+ scope << ' a'
17
+ page.should have_css(scope, :text => label)
18
+ section = find(scope, :text => label)
19
+ opens = section["data-navy-opens"]
20
+ end
21
+ section.click
22
+ end
23
+ end
24
+
25
+ When /^I expand the "([^"]*)" (?:section|tab)$/ do |label|
26
+ page.execute_script("$('.navy-level-1 .navy-section:contains(\"#{label}\") .navy-section-expander').click()")
27
+ end
28
+
29
+ Then /^there should( not)? be a "([^"]*)" (?:section|tab)$/ do |negate, label|
30
+ expectation = negate ? "should_not" : "should"
31
+ page.send(expectation, have_css(".navy-navigation", :text => label))
32
+ end
33
+
34
+ Then /^I should be in the "([^"]*)" (?:section|tab)$/ do |label|
35
+ page.should have_css(".navy-navigation .navy-active", :text => label)
36
+ end
@@ -0,0 +1,81 @@
1
+ (function() {
2
+
3
+ function unexpand($navigation, level) {
4
+ $navigation.find('.navy-navigation-bar[data-navy-navigation-level="' + (level + 1) + '"].navy-hidden').hide();
5
+ $navigation.find('.navy-navigation-bar[data-navy-navigation-level="' + (level + 1) + '"].navy-current').show();
6
+ var $thisBar = $navigation.find('.navy-navigation-bar[data-navy-navigation-level="' + level + '"]');
7
+ $thisBar.find('.navy-section-expanded').removeClass('navy-section-expanded navy-active');
8
+ $thisBar.find('.navy-section.navy-current').addClass('navy-active');
9
+ }
10
+
11
+ function expand($navigation, level, idToOpen) {
12
+ unexpand($navigation, level);
13
+ $navigation.find('.navy-navigation-bar[data-navy-navigation-level="' + (level + 1) + '"]').hide();
14
+ $navigation.find('.navy-navigation-bar[data-navy-opened-by="' + idToOpen + '"][data-navy-navigation-level="' + (level + 1) + '"]').show();
15
+ var $thisBar = $navigation.find('.navy-navigation-bar[data-navy-navigation-level="' + level + '"]');
16
+ $thisBar.find('.navy-current').removeClass('navy-active');
17
+ }
18
+
19
+ function layout() {
20
+ $('.navy-navigation .navy-navigation-dropdown').each(function() {
21
+ var $dropdown = $(this);
22
+ var $bar = $dropdown.closest('.navy-navigation-bar');
23
+ var $layouted_sections = $bar.find('.navy-layouted-sections');
24
+ var $dropdown_sections = $bar.find('.navy-dropdown-sections');
25
+ $dropdown_sections.find('.navy-section').appendTo($layouted_sections);
26
+ if ( $layouted_sections.find('.navy-section:last').offset().top > $layouted_sections.find('.navy-section:first').offset().top ) {
27
+ var barTop = $bar.offset().top;
28
+ while ( $dropdown.offset().top > barTop ) {
29
+ var $last_section = $layouted_sections.find('.navy-section:not(.navy-current):last')
30
+ $last_section.detach().prependTo($dropdown_sections);
31
+ if ( $last_section.length == 0 ) break;
32
+ }
33
+ }
34
+ if ( $dropdown_sections.find('.navy-section').length > 0 ) {
35
+ $dropdown.css('visibility', 'visible');
36
+ } else {
37
+ $dropdown.css('visibility', 'hidden');
38
+ }
39
+ });
40
+ }
41
+
42
+ function sectionExpanderClicked() {
43
+ var $section = $(this).closest('.navy-section');
44
+ var level = Number($section.closest('.navy-navigation-bar').data('navy-navigation-level'));
45
+ var openId = $section.data('navy-opens');
46
+ if ( $section.hasClass('navy-section-expanded') ) {
47
+ unexpand($section.closest('.navy-navigation'), level, openId);
48
+ } else {
49
+ expand($section.closest('.navy-navigation'), level, openId);
50
+ $section.addClass('navy-section-expanded navy-active');
51
+ }
52
+ return false;
53
+ }
54
+
55
+ function navigationDropdownClicked() {
56
+ var $dropdownExpander = $(this);
57
+ var $dropdown = $dropdownExpander.closest('.navy-navigation-dropdown');
58
+ if ( $dropdownExpander.hasClass('navy-section-expanded') ) {
59
+ $dropdownExpander.removeClass('navy-section-expanded navy-active');
60
+ $dropdown.find('.navy-dropdown-sections').hide();
61
+ } else {
62
+ $dropdownExpander.addClass('navy-section-expanded navy-active');
63
+ $dropdown.find('.navy-dropdown-sections').show();
64
+ }
65
+ }
66
+
67
+ function init() {
68
+ $(function() {
69
+ $('.navy-navigation .navy-section-expander').live('click', sectionExpanderClicked);
70
+
71
+ // difficult to handle in selenium, since it now depends on browser width
72
+ $('.navy-navigation .navy-dropdown-expander').live('click', navigationDropdownClicked);
73
+
74
+ $(window).resize(layout);
75
+
76
+ layout();
77
+ });
78
+ }
79
+
80
+ return { init: init, unexpand: unexpand };
81
+ })().init();
data/lib/navy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Navy
2
- VERSION = "0.1"
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: makandra-navy
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- version: "0.1"
9
+ - 1
10
+ version: 0.1.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Tobias Kraze
@@ -15,7 +16,7 @@ autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2012-04-13 00:00:00 Z
19
+ date: 2012-04-20 00:00:00 Z
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: actionpack
@@ -43,7 +44,10 @@ extra_rdoc_files: []
43
44
  files:
44
45
  - .gitignore
45
46
  - Gemfile
47
+ - README.markdown
46
48
  - Rakefile
49
+ - assets/cucumber/navy_steps.rb
50
+ - assets/javascripts/navy.js
47
51
  - assets/sass/_navy.sass
48
52
  - lib/navy.rb
49
53
  - lib/navy/description.rb