navtastic 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -1
- data/.yardopts +1 -0
- data/CHANGELOG.md +11 -1
- data/README.md +146 -6
- data/docs/bulma_headers_preview.png +0 -0
- data/docs/foundation_styles_preview.png +0 -0
- data/lib/navtastic.rb +17 -1
- data/lib/navtastic/configuration.rb +84 -0
- data/lib/navtastic/item.rb +44 -3
- data/lib/navtastic/menu.rb +52 -9
- data/lib/navtastic/renderer.rb +61 -38
- data/lib/navtastic/renderer/bootstrap4.rb +35 -0
- data/lib/navtastic/renderer/bulma.rb +44 -0
- data/lib/navtastic/renderer/foundation6.rb +58 -0
- data/lib/navtastic/renderer/simple.rb +18 -0
- data/lib/navtastic/version.rb +1 -1
- data/spec/demo/index.rhtml +7 -20
- data/spec/demo/renderer/bootstrap4.rhtml +55 -0
- data/spec/demo/renderer/bulma.rhtml +70 -0
- data/spec/demo/renderer/foundation6.rhtml +81 -0
- data/spec/demo/renderer/simple.rhtml +41 -0
- data/spec/demo/server.rb +21 -10
- data/spec/navtastic/configuration_spec.rb +71 -0
- data/spec/navtastic/item_spec.rb +82 -0
- data/spec/navtastic/menu_spec.rb +107 -3
- data/spec/navtastic/renderer/simple_spec.rb +19 -0
- data/spec/navtastic/renderer_spec.rb +49 -7
- data/spec/navtastic_spec.rb +41 -4
- data/spec/spec_helper.rb +2 -1
- data/spec/support/navtastic_helpers.rb +17 -0
- data/spec/support/{navtastic_store.rb → navtastic_reset.rb} +2 -1
- metadata +25 -4
data/lib/navtastic/menu.rb
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
module Navtastic
|
2
2
|
# Stores items generated by a definition block
|
3
3
|
class Menu
|
4
|
+
# Configuration settings per menu
|
5
|
+
class Configuration
|
6
|
+
# @return [String,nil] a url to prepend every item url with
|
7
|
+
attr_accessor :base_url
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@base_url = nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
4
14
|
include Enumerable
|
5
15
|
|
6
16
|
# @return [Array<Item>] the items in this menu
|
@@ -9,15 +19,18 @@ module Navtastic
|
|
9
19
|
# @return [Menu,nil] this parent of this menu
|
10
20
|
attr_reader :parent
|
11
21
|
|
22
|
+
# @return [Menu::Configuration] the configuration for this menu
|
23
|
+
attr_reader :config
|
24
|
+
|
12
25
|
# Create a new empty menu
|
13
26
|
#
|
14
|
-
# @param
|
27
|
+
# @param parent [Menu] the parent menu of this is submenu
|
15
28
|
def initialize(parent = nil)
|
16
29
|
@parent = parent
|
17
30
|
|
31
|
+
@config = Menu::Configuration.new
|
18
32
|
@current_item = nil
|
19
33
|
@items = []
|
20
|
-
@items_by_url = {}
|
21
34
|
end
|
22
35
|
|
23
36
|
# @return [true] if this menu is the root menu
|
@@ -43,14 +56,24 @@ module Navtastic
|
|
43
56
|
#
|
44
57
|
# @param name [String]the name to display in the menu
|
45
58
|
# @param url [String] the url to link to, if the item is a link
|
59
|
+
# @param options [Hash] extra confiration options
|
46
60
|
#
|
47
61
|
# @yield [submenu] block to generate a sub menu
|
48
62
|
# @yieldparam submenu [Menu] the menu to be initialized
|
49
|
-
def item(name, url = nil)
|
50
|
-
|
63
|
+
def item(name, url = nil, options = {})
|
64
|
+
# If only options were given and no url, move options to the right place
|
65
|
+
if url.is_a?(Hash) && options.empty?
|
66
|
+
options = url
|
67
|
+
url = nil
|
68
|
+
end
|
69
|
+
|
70
|
+
item = Item.new(self, name, url, options)
|
51
71
|
|
52
72
|
if block_given?
|
53
73
|
submenu = Menu.new(self)
|
74
|
+
|
75
|
+
submenu.config.base_url = url if options[:base_url] && url
|
76
|
+
|
54
77
|
yield submenu
|
55
78
|
item.submenu = submenu
|
56
79
|
end
|
@@ -74,7 +97,7 @@ module Navtastic
|
|
74
97
|
# @return [Item] if an item with that url exists
|
75
98
|
# @return [nil] if the item doens't exist
|
76
99
|
def [](url)
|
77
|
-
|
100
|
+
items_by_url[url]
|
78
101
|
end
|
79
102
|
|
80
103
|
# Sets the current active item by url
|
@@ -87,7 +110,7 @@ module Navtastic
|
|
87
110
|
return if current_url.nil?
|
88
111
|
|
89
112
|
# Sort urls from longest to shortest and find the first matching substring
|
90
|
-
matching_item =
|
113
|
+
matching_item = items_by_url
|
91
114
|
.sort_by { |url, _item| -url.length }.to_h
|
92
115
|
.find { |url, _item| current_url.start_with? url }
|
93
116
|
|
@@ -106,6 +129,17 @@ module Navtastic
|
|
106
129
|
end
|
107
130
|
end
|
108
131
|
|
132
|
+
# The base url of this menu, including all parent base urls
|
133
|
+
#
|
134
|
+
# @return [String]
|
135
|
+
def base_url
|
136
|
+
base_url = config.base_url.to_s
|
137
|
+
base_url.prepend Navtastic.configuration.base_url.to_s if root?
|
138
|
+
base_url.prepend @parent.base_url.to_s unless root?
|
139
|
+
base_url = nil if base_url.empty?
|
140
|
+
base_url
|
141
|
+
end
|
142
|
+
|
109
143
|
protected
|
110
144
|
|
111
145
|
# Register a newly added item
|
@@ -114,14 +148,23 @@ module Navtastic
|
|
114
148
|
def register_item(item)
|
115
149
|
return unless item.url
|
116
150
|
|
117
|
-
@items_by_url[item.url] = item
|
118
|
-
|
119
151
|
if root?
|
120
152
|
# The first item with a url is the default current item
|
121
|
-
@current_item = item if @current_item.nil?
|
153
|
+
@current_item = item if @current_item.nil?
|
122
154
|
else
|
123
155
|
@parent.register_item(item)
|
124
156
|
end
|
125
157
|
end
|
158
|
+
|
159
|
+
def items_by_url
|
160
|
+
indexed_items = {}
|
161
|
+
|
162
|
+
@items.each do |item|
|
163
|
+
indexed_items[item.url] = item if item.url?
|
164
|
+
indexed_items.merge!(item.submenu.items_by_url) if item.submenu?
|
165
|
+
end
|
166
|
+
|
167
|
+
indexed_items
|
168
|
+
end
|
126
169
|
end
|
127
170
|
end
|
data/lib/navtastic/renderer.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'arbre'
|
2
2
|
|
3
3
|
module Navtastic
|
4
|
-
# Generate HTML based on a menu
|
4
|
+
# Generate HTML based on a menu.
|
5
|
+
#
|
6
|
+
# This base renderer only generates a structure and no css classes.
|
5
7
|
#
|
6
8
|
# The actual HTML generation is done using the
|
7
9
|
# [Arbre](https://github.com/activeadmin/arbre) gem.
|
@@ -11,39 +13,32 @@ module Navtastic
|
|
11
13
|
# Create a new renderer
|
12
14
|
#
|
13
15
|
# @param menu [Menu]
|
16
|
+
# @param options [Hash]
|
14
17
|
#
|
15
|
-
# @return [
|
16
|
-
def self.render(menu)
|
17
|
-
new(menu:
|
18
|
-
|
18
|
+
# @return [Self]
|
19
|
+
def self.render(menu, options = {})
|
20
|
+
new(root: menu, options: options) do
|
21
|
+
render_menu(root)
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
22
|
-
#
|
25
|
+
# Start a new root menu or submenu (e.g. `<ul>` tag)
|
23
26
|
#
|
24
27
|
# @param menu [Menu]
|
25
28
|
# @return [Arbre::HTML::Tag]
|
26
|
-
def
|
27
|
-
ul
|
28
|
-
menu.each do |item|
|
29
|
-
item_container item
|
30
|
-
end
|
31
|
-
end
|
29
|
+
def menu_tag(menu) # rubocop:disable Lint/UnusedMethodArgument
|
30
|
+
ul { yield }
|
32
31
|
end
|
33
32
|
|
34
33
|
# The container for every menu item (e.g. `<li>` tags)
|
35
34
|
#
|
36
35
|
# @param item [Item]
|
37
36
|
# @return [Arbre::HTML::Tag]
|
38
|
-
def
|
39
|
-
li
|
40
|
-
item_content item
|
41
|
-
|
42
|
-
menu(item.submenu) if item.submenu?
|
43
|
-
end
|
37
|
+
def item_tag(item) # rubocop:disable Lint/UnusedMethodArgument
|
38
|
+
li { yield }
|
44
39
|
end
|
45
40
|
|
46
|
-
# The item itself (e.g. `<a>` tag for links)
|
41
|
+
# The item itself (e.g. `<a>` tag for links or `<span>` for text)
|
47
42
|
#
|
48
43
|
# @param item [Item]
|
49
44
|
# @return [Arbre::HTML::Tag]
|
@@ -51,37 +46,65 @@ module Navtastic
|
|
51
46
|
if item.url
|
52
47
|
a(href: item.url) { item.name }
|
53
48
|
else
|
54
|
-
span item.name
|
49
|
+
span { item.name }
|
55
50
|
end
|
56
51
|
end
|
57
52
|
|
58
|
-
#
|
53
|
+
# Check if a submenu should be displayed inside the item container of after
|
54
|
+
# it.
|
59
55
|
#
|
60
|
-
#
|
61
|
-
# the current active item.
|
56
|
+
# Defaults to `true`.
|
62
57
|
#
|
63
|
-
# @param item [Item]
|
64
|
-
# @
|
58
|
+
# @param item [Item]
|
59
|
+
# @return [bool]
|
60
|
+
def menu_inside_container?(item) # rubocop:disable Lint/UnusedMethodArgument
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# Render the menu structure
|
65
67
|
#
|
66
|
-
# @
|
67
|
-
|
68
|
-
|
68
|
+
# @param menu [Menu]
|
69
|
+
# @return [Arbre::HTML::Tag]
|
70
|
+
def render_menu(menu)
|
71
|
+
menu_tag(menu) do
|
72
|
+
menu.each do |item|
|
73
|
+
render_item(item)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
69
77
|
|
70
|
-
|
71
|
-
|
72
|
-
|
78
|
+
# Render the item structure
|
79
|
+
#
|
80
|
+
# @param item [Item]
|
81
|
+
# @return [Arbre::HTML::Tag]
|
82
|
+
def render_item(item)
|
83
|
+
element = item_tag(item) do
|
84
|
+
render_item_content(item)
|
73
85
|
end
|
74
86
|
|
75
|
-
classes
|
87
|
+
# Add custom css classes to the element
|
88
|
+
element.class_list << item.options[:class] if item.options[:class]
|
89
|
+
|
90
|
+
return unless item.submenu? && !menu_inside_container?(item)
|
91
|
+
render_menu(item.submenu)
|
76
92
|
end
|
77
93
|
|
78
|
-
#
|
94
|
+
# Render the item content
|
79
95
|
#
|
80
|
-
# @
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
96
|
+
# @param item [Item]
|
97
|
+
# @return [Arbre::HTML::Tag]
|
98
|
+
def render_item_content(item)
|
99
|
+
element = item_content(item)
|
100
|
+
|
101
|
+
# Add custom css classes to the element
|
102
|
+
if item.options[:content_class] && element.respond_to?(:class_list)
|
103
|
+
element.class_list << item.options[:content_class]
|
104
|
+
end
|
105
|
+
|
106
|
+
return unless item.submenu? && menu_inside_container?(item)
|
107
|
+
render_menu(item.submenu)
|
85
108
|
end
|
86
109
|
end
|
87
110
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'arbre'
|
2
|
+
|
3
|
+
module Navtastic
|
4
|
+
class Renderer
|
5
|
+
# This renderer adds css classes and structure for the bootstrap 4
|
6
|
+
# framework
|
7
|
+
class Bootstrap4 < Navtastic::Renderer
|
8
|
+
def menu_tag(_menu)
|
9
|
+
class_list = ['nav']
|
10
|
+
class_list << 'flex-column' if vertical?
|
11
|
+
|
12
|
+
ul(class: class_list.join(' ')) { yield }
|
13
|
+
end
|
14
|
+
|
15
|
+
def item_tag(item)
|
16
|
+
element = super(item)
|
17
|
+
element.class_list << 'nav-item'
|
18
|
+
element
|
19
|
+
end
|
20
|
+
|
21
|
+
def item_content(item)
|
22
|
+
element = super(item)
|
23
|
+
element.class_list << 'nav-link'
|
24
|
+
element.class_list << 'active' if item.current?
|
25
|
+
element
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def vertical?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'arbre'
|
2
|
+
|
3
|
+
module Navtastic
|
4
|
+
class Renderer
|
5
|
+
# This renderer adds css classes and structure for the bulma.io framework
|
6
|
+
# @see file:README.md#Bulma_Configuration documentation on bulma renderer
|
7
|
+
# options
|
8
|
+
class Bulma < Navtastic::Renderer
|
9
|
+
def submenu_inside_container?(item)
|
10
|
+
!(headers? && item.menu.root?)
|
11
|
+
end
|
12
|
+
|
13
|
+
def menu_tag(menu)
|
14
|
+
if headers? && menu.root?
|
15
|
+
nav(class: 'menu') { yield }
|
16
|
+
else
|
17
|
+
ul(class: 'menu-list') { yield }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def item_tag(item)
|
22
|
+
if headers? && item.menu.root?
|
23
|
+
para(class: 'menu-label') { yield }
|
24
|
+
else
|
25
|
+
li { yield }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def item_content(item)
|
30
|
+
element = super(item)
|
31
|
+
|
32
|
+
element.class_list << 'is-active' if item.current?
|
33
|
+
|
34
|
+
element
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def headers?
|
40
|
+
options[:headers]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'arbre'
|
2
|
+
|
3
|
+
module Navtastic
|
4
|
+
class Renderer
|
5
|
+
# This renderer adds css classes and structure for the foundation 6
|
6
|
+
# framework
|
7
|
+
# @see file:README.md#Foundation_Configuration documentation on foundation
|
8
|
+
# renderer options
|
9
|
+
class Foundation6 < Navtastic::Renderer
|
10
|
+
def menu_tag(menu)
|
11
|
+
class_list = ['menu']
|
12
|
+
class_list << 'vertical' if vertical?
|
13
|
+
class_list << 'nested' unless menu.root?
|
14
|
+
|
15
|
+
list = ul(class: class_list.join(' ')) { yield }
|
16
|
+
|
17
|
+
if drilldown? && menu.root?
|
18
|
+
list.class_list << 'drilldown'
|
19
|
+
list.set_attribute('data-drilldown', true)
|
20
|
+
end
|
21
|
+
|
22
|
+
list
|
23
|
+
end
|
24
|
+
|
25
|
+
def item_tag(item)
|
26
|
+
element = super(item)
|
27
|
+
element.class_list << 'is-active' if item.current?
|
28
|
+
element
|
29
|
+
end
|
30
|
+
|
31
|
+
def item_content(item)
|
32
|
+
element = if item.url?
|
33
|
+
a(href: item.url) { item.name }
|
34
|
+
elsif drilldown?
|
35
|
+
a(href: '#') { item.name }
|
36
|
+
else
|
37
|
+
span(class: 'menu-text') { item.name }
|
38
|
+
end
|
39
|
+
|
40
|
+
if drilldown? && item.active? && options[:active_class]
|
41
|
+
element.class_list << options[:active_class]
|
42
|
+
end
|
43
|
+
|
44
|
+
element
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def vertical?
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
def drilldown?
|
54
|
+
options[:style] == :drilldown
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'arbre'
|
2
|
+
|
3
|
+
module Navtastic
|
4
|
+
class Renderer
|
5
|
+
# This renderer only adds a `current` css class to the current item
|
6
|
+
class Simple < Navtastic::Renderer
|
7
|
+
def item_tag(item)
|
8
|
+
li(class: current_css_class(item)) { yield }
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def current_css_class(item)
|
14
|
+
item.current? ? 'current' : nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/navtastic/version.rb
CHANGED
data/spec/demo/index.rhtml
CHANGED
@@ -1,29 +1,16 @@
|
|
1
|
-
<%
|
2
|
-
Navtastic.define :main_menu do |menu|
|
3
|
-
menu.item "Home", '/' do |submenu|
|
4
|
-
submenu.item "Posts", '/posts'
|
5
|
-
submenu.item "About", '/about'
|
6
|
-
end
|
7
|
-
|
8
|
-
menu.item "Settings" do |submenu|
|
9
|
-
submenu.item "General", '/settings'
|
10
|
-
submenu.item "Profile", '/settings/profile'
|
11
|
-
end
|
12
|
-
end
|
13
|
-
%>
|
14
|
-
|
15
1
|
<html>
|
16
2
|
<head>
|
17
3
|
<title>Navtastic Demo Server</title>
|
18
|
-
<style type="text/css">
|
19
|
-
.current { font-weight: bold }
|
20
|
-
.current ul { font-weight: normal }
|
21
|
-
</style>
|
22
4
|
</head>
|
23
5
|
|
24
6
|
<body>
|
25
|
-
|
7
|
+
<h1>Renderers:</h1>
|
26
8
|
|
27
|
-
<
|
9
|
+
<ul>
|
10
|
+
<li><a href="simple">Simple</a></li>
|
11
|
+
<li><a href="bootstrap4">Bootstrap4</a></li>
|
12
|
+
<li><a href="bulma">Bulma</a></li>
|
13
|
+
<li><a href="foundation6">Foundation6</a></li>
|
14
|
+
</ul>
|
28
15
|
</body>
|
29
16
|
</html>
|