navtastic 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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>
|