antw-kin 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,14 @@
1
+ == 0.3.3 2009-08-28
2
+
3
+ * Added Subnav and HasRight menu formatters.
4
+ * A custom formatter can now be supplied when setting up a menu.
5
+
6
+ == 0.3.2 2009-08-27
7
+
8
+ * The _modal.sass partial, containing shared styles for modal dialogs, has
9
+ been added to assets/css.
10
+ * Added a block syntax for defining nav items.
11
+
1
12
  == 0.3.1 2009-08-26
2
13
 
3
14
  * When setting on which controller and actions a nav item can be active, you
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 2
2
+ :patch: 3
3
3
  :major: 0
4
4
  :minor: 3
data/lib/kin.rb CHANGED
@@ -14,10 +14,13 @@ if defined?(Merb::Plugins)
14
14
  require File.join(kin, 'nav')
15
15
 
16
16
  Merb::Plugins.add_rakefiles(File.join(kin, 'tasks', 'sync_assets'))
17
+ Merb::Plugins.add_rakefiles(File.join(kin, 'tasks', 'sprites'))
17
18
 
18
19
  # Default nav formatter. Can be overridden in an after_app_loads block, or
19
20
  # on a case-by-case basis in +display_navigation+.
20
- Merb::Plugins.config[:kin] = { :nav_formatter => Kin::Nav::BasicFormatter }
21
+ Merb::Plugins.config[:kin] = {
22
+ :nav_formatter => Kin::Nav::Formatters::Basic
23
+ }
21
24
 
22
25
  Merb::BootLoader.after_app_loads do
23
26
  # Add default time formats.
data/lib/kin/masthead.rb CHANGED
@@ -22,20 +22,38 @@ module Kin
22
22
  class Builder
23
23
  include Merb::Helpers::Tag
24
24
 
25
- DEFAULT_CLASSES = {
25
+ CSS_CLASSES = {
26
26
  :title => ''.freeze,
27
27
  :subtitle => 'subtitle'.freeze,
28
28
  :right_title => 'main'.freeze,
29
29
  :right_subtitle => 'subtitle'.freeze
30
30
  }.freeze
31
31
 
32
- attr_accessor :no_border
33
- attr_reader :options
34
-
32
+ ##
33
+ # Creates a new masthead builder.
34
+ #
35
+ # @api semipublic
36
+ #
35
37
  def initialize
36
- @title, @right_title = nil, nil
37
- @subtitle, @right_subtitle = nil, nil
38
- @options = {}
38
+ @title = @right_title = @subtitle = @right_subtitle = nil
39
+ @options = Hash.new { |hash, key| hash[key] = {} }
40
+ end
41
+
42
+ ##
43
+ # Builds a masthead.
44
+ #
45
+ # Yields the builder instance providing a DSL for setting up the
46
+ # masthead as desired.
47
+ #
48
+ # @return [Kin::Nav::Builder]
49
+ #
50
+ # @yield [Kin::Nav::Builder]
51
+ #
52
+ # @api semipublic
53
+ #
54
+ def build
55
+ yield self
56
+ self
39
57
  end
40
58
 
41
59
  ##
@@ -46,6 +64,8 @@ module Kin
46
64
  # @param [Hash] options A hash containing extraoptions.
47
65
  # @return [String] The masthead title.
48
66
  #
67
+ # @api public
68
+ #
49
69
  def title(value = nil, options = nil)
50
70
  end
51
71
 
@@ -57,6 +77,8 @@ module Kin
57
77
  # @param [Hash] options A hash containing extraoptions.
58
78
  # @return [String] The masthead right title.
59
79
  #
80
+ # @api public
81
+ #
60
82
  def right_title(value = nil, options = nil)
61
83
  end
62
84
 
@@ -68,6 +90,8 @@ module Kin
68
90
  # @param [Hash] options A hash containing extraoptions.
69
91
  # @return [String] The masthead subtitle.
70
92
  #
93
+ # @api public
94
+ #
71
95
  def subtitle(value = nil, options = nil)
72
96
  end
73
97
 
@@ -79,6 +103,8 @@ module Kin
79
103
  # @param [Hash] options A hash containing extraoptions.
80
104
  # @return [String] The masthead right subtitle.
81
105
  #
106
+ # @api public
107
+ #
82
108
  def right_subtitle(value = nil, options = nil)
83
109
  end
84
110
 
@@ -101,89 +127,114 @@ module Kin
101
127
  end
102
128
 
103
129
  ##
104
- # Returns the masthead as HTML.
105
- # @return [String] The masthead with all the various titles.
130
+ # Returns the HTML representation of the masthead.
131
+ #
132
+ # @return [String]
133
+ # The masthead with all the various titles.
134
+ #
135
+ # @api public
106
136
  #
107
137
  def to_html
108
- formatted_title = formatted(:title, :h1)
109
- formatted_subtitle = formatted(:subtitle) if @subtitle
110
-
111
- extras = if has_extras?
112
- '<div class="extra">%s %s</div>' % [
113
- formatted(:right_title) || '<span class="main">&nbsp;</span>',
114
- @right_subtitle ? formatted(:right_subtitle) : ''
115
- ]
116
- end
117
-
118
138
  <<-HTML
119
- <div id="masthead"#{no_border ? ' class="no_border"' : ''}>
139
+ <div id="masthead">
120
140
  <div class="details">
121
- #{formatted_title}
122
- #{formatted_subtitle}
141
+ #{ formatted(:title, :h1) }
142
+ #{ formatted(:subtitle) }
123
143
  </div>
124
144
 
125
- #{extras}
145
+ #{ extras_as_html }
126
146
  </div>
127
147
  HTML
128
148
  end
129
149
 
130
- ##
131
- # Builds a masthead.
132
- #
133
- def build
134
- yield self
135
- self
136
- end
137
-
138
- #######
139
- private
140
- #######
150
+ private # ==============================================================
141
151
 
142
152
  ##
143
- # Returns whether this masthead builder has an "extras".
153
+ # Returns <div.extra> containing the right-hand stuff.
144
154
  #
145
- # Extras are defined as being a right-hand title or right-hand subtitle.
146
- # Generally, if the builder has no extras, the .extras div will not be
147
- # rendered by #to_html
155
+ # @return [String, nil]
156
+ # Returns nil if no <div.extra> is needed.
148
157
  #
149
- # @return [TrueClass|FalseClass]
158
+ # @api private
150
159
  #
151
- def has_extras?
152
- not @right_title.nil? or not @right_subtitle.nil?
160
+ def extras_as_html
161
+ unless @right_title.nil? && @right_subtitle.nil?
162
+ <<-HTML
163
+ <div class="extra">
164
+ #{ formatted(:right_title) }
165
+ #{ formatted(:right_subtitle) }
166
+ </div>
167
+ HTML
168
+ end
153
169
  end
154
170
 
155
171
  ##
156
- # Returns formatted text for a given field. Wraps the text in a link if
157
- # one is required, otherwise the text is returned on it's own.
172
+ # Returns the HTML element containing the value for the field specified
173
+ # by +field+.
174
+ #
175
+ # @param [Symbol] field
176
+ # The name of the field whose value you wish to retrieve.
177
+ # @param [Symbol] wrap_in
178
+ # The HTML element which should wrap the field value.
179
+ #
180
+ # @return [String, nil]
181
+ # nil will be returned if no the field should not be displayed.
182
+ #
183
+ # @api semipublic
158
184
  #
159
185
  def formatted(field, wrap_in = :span)
160
- if instance_variable_get(:"@#{field}").blank?
161
- return nil
162
- else
163
- value = instance_variable_get(:"@#{field}").to_s
164
- end
186
+ value = value_for(field)
187
+ options = @options[field]
165
188
 
166
- options = @options[field] || {}
167
- link = options.delete(:link)
189
+ if value.blank?
190
+ # If the field is blank, we want to return _unless_ the field is the
191
+ # right subtitle, and a right title is set (in which case a title
192
+ # element is needed to push the subtitle down).
193
+ if field != :right_title || value_for(:right_subtitle).blank?
194
+ return nil
195
+ else
196
+ value = '&nbsp;'
197
+ options[:no_escape] = true
198
+ end
199
+ end
168
200
 
169
201
  # Escape required?
170
202
  unless options.delete(:no_escape)
171
- value = Merb::Parse.escape_xml(value.to_s)
203
+ value = Merb::Parse.escape_xml(value)
172
204
  end
173
205
 
174
206
  # Link required?
175
- if link
176
- value = '<a href="%s" title="%s">%s</a>' % [link, value, value]
207
+ if options.has_key?(:link)
208
+ value = %[<a href="#{options.delete(:link)}"] +
209
+ %[ title="#{value}">#{value}</a>]
177
210
  end
178
211
 
212
+ # Set the CSS class.
179
213
  if options[:class]
180
- options[:class] += ' %s' % DEFAULT_CLASSES[field]
181
- elsif field != :title
182
- options[:class] = DEFAULT_CLASSES[field]
214
+ options[:class] = [options[:class], CSS_CLASSES[field]].join(' ')
215
+ elsif CSS_CLASSES[field] != ''
216
+ options[:class] = CSS_CLASSES[field]
183
217
  end
184
218
 
185
219
  tag(wrap_in, value, options)
186
220
  end
221
+
222
+ ##
223
+ # Retrieves a value for the specified +field+.
224
+ #
225
+ # @param [Symbol] field
226
+ # The name of the field whose value you wish to retrieve.
227
+ #
228
+ # @return [String, nil]
229
+ # Returns nil if nothing is set, or a string version of whatever the
230
+ # field is set to.
231
+ #
232
+ # @api private
233
+ #
234
+ def value_for(field)
235
+ value = instance_variable_get(:"@#{field}")
236
+ value.nil? ? nil : value.to_s
237
+ end
187
238
  end
188
239
 
189
240
  ##
@@ -199,22 +250,24 @@ module Kin
199
250
  # Note that the block is evaluated within the MastheadBuilder instance
200
251
  # itself.
201
252
  #
202
- # @example [In a template]
203
- # - masthead(:no_border => true) do
253
+ # @example In a template
254
+ # - masthead do
204
255
  # - title @set
205
256
  # - subtitle "This set contains #{@set.contents}"
206
257
  #
258
+ # @api public
259
+ #
207
260
  def masthead(options = {}, &blk)
208
- masthead_builder.no_border = options.fetch(:no_border, false)
209
261
  masthead_builder.build(&blk)
210
262
  end
211
263
 
212
264
  ##
213
265
  # Returns the MastheadBuilder instance for the current request.
214
266
  #
215
- # @api private
216
267
  # @return [Kin::MastheadBuilder]
217
268
  #
269
+ # @api public
270
+ #
218
271
  def masthead_builder
219
272
  @_masthead_builder ||= Kin::Masthead::Builder.new
220
273
  end
data/lib/kin/nav.rb CHANGED
@@ -19,6 +19,8 @@ module Kin
19
19
  #
20
20
  # @param [Symbol] name
21
21
  # A name for this menu.
22
+ # @param [Kin::Nav::Formatters::Basic] formatter
23
+ # A custom formatter to use when rendering the menu as HTML.
22
24
  # @param [Block] blk
23
25
  # A block for setting up the menu.
24
26
  #
@@ -27,8 +29,8 @@ module Kin
27
29
  #
28
30
  # @api public
29
31
  #
30
- def self.setup(name = :default, &blk)
31
- menu = Builder.new(name).build(&blk)
32
+ def self.setup(name = :default, formatter = nil, &blk)
33
+ menu = Builder.new(name, formatter).build(&blk)
32
34
  menu.freeze
33
35
  menu.items.freeze
34
36
  menu.items.each { |i| i.freeze }
@@ -88,17 +90,31 @@ module Kin
88
90
  #
89
91
  # @param [Symbol] name
90
92
  # A name for this nav.
93
+ # @param [Kin::Nav::Formatters::Basic]
94
+ # A formatter to be used when rendering the menu.
91
95
  #
92
96
  # @api private
93
97
  #
94
- def initialize(name)
98
+ def initialize(name, formatter = nil)
95
99
  @name = name.to_sym
100
+ @formatter = formatter
96
101
  @items = []
97
102
 
98
103
  # Used to find the active item on a given page.
99
104
  @matchers = Struct.new(:best, :controller, :generic).new([], [], [])
100
105
  end
101
106
 
107
+ ##
108
+ # Returns the default formatter to be used when rendering this menu.
109
+ #
110
+ # @return [Kin::Nav::Formatters::Basic]
111
+ #
112
+ # @api public
113
+ #
114
+ def formatter
115
+ @formatter || Merb::Plugins.config[:kin][:nav_formatter]
116
+ end
117
+
102
118
  ##
103
119
  # Adds a controller name / action name pair for matching active items.
104
120
  #
@@ -109,7 +125,7 @@ module Kin
109
125
  #
110
126
  # @return Kin::Nav::ItemMatcher
111
127
  #
112
- # @api semipubic
128
+ # @api semipublic
113
129
  #
114
130
  def add_active_match(name, item)
115
131
  name = name.split('/')
@@ -228,6 +244,8 @@ module Kin
228
244
  # Returns true if this item expects a resource in order to generate a
229
245
  # URL.
230
246
  #
247
+ # @api private
248
+ #
231
249
  def expects_resource?
232
250
  not @resource.nil?
233
251
  end
@@ -235,6 +253,8 @@ module Kin
235
253
  ##
236
254
  # Takes a resource and generates a URL.
237
255
  #
256
+ # @api private
257
+ #
238
258
  def resource_url(given)
239
259
  raise MissingResource, "Nav item #{id.inspect} expected a " \
240
260
  "resource to generate a URL, and none was given" unless given
@@ -255,6 +275,11 @@ module Kin
255
275
  # given controller and action.
256
276
  #
257
277
  class ItemMatcher
278
+ ##
279
+ # Returns the item which this matcher is associated.
280
+ #
281
+ # @api semipublic
282
+ #
258
283
  attr_reader :item
259
284
 
260
285
  ##
@@ -10,11 +10,13 @@ module Kin
10
10
  #
11
11
  # @param [Symbol] name
12
12
  # A unique name for the Menu instance.
13
+ # @param [Kin::Nav::Formatters::Basic] formatter
14
+ # A custom formatter to use when rendering the menu as HTML.
13
15
  #
14
16
  # @api private
15
17
  #
16
- def initialize(name)
17
- @nav = Menu.new(name)
18
+ def initialize(name, formatter = nil)
19
+ @nav = Menu.new(name, formatter)
18
20
  @item_builders = []
19
21
  end
20
22
 
@@ -1,116 +1,211 @@
1
1
  module Kin
2
2
  module Nav
3
-
4
- ##
5
- # Receives a nav instance and transforms it into HTML.
6
- #
7
- # @see Kin::Nav::Helper#display_navigation
8
- #
9
- class BasicFormatter
10
- include Merb::Helpers::Tag
11
-
3
+ module Formatters
12
4
  ##
13
- # Creates a new BasicFormatter instance.
14
- #
15
- # @param [Kin::Nav::Menu] nav
16
- # An instance of a Menu to be rendered as HTML.
17
- # @param [#controller_name, #action_name] controller
18
- # An object which behaves like a controller.
19
- # @param [Hash] options
20
- # A hash of options for customising the menu. See
21
- # Kin::Nav::Helper#display_navigation.
5
+ # Receives a nav instance and transforms it into HTML.
22
6
  #
23
- # @api public
7
+ # @see Kin::Nav::Helper#display_navigation
24
8
  #
25
- def initialize(nav, controller, options)
26
- @nav = nav
27
- @current = nav.active_item(controller)
28
-
29
- @resource = options[:resource]
30
- @inject = options.fetch(:inject, {})
31
- @guards = options.fetch(:guard, {})
32
-
33
- # Escape injected content.
34
- @inject.each do |item_id, contents|
35
- contents = Array(contents)
36
- contents.map! { |v| Merb::Parse.escape_xml(v) }
37
- @inject[item_id] = contents
9
+ class Basic
10
+ include Merb::Helpers::Tag
11
+
12
+ ##
13
+ # Creates a new BasicFormatter instance.
14
+ #
15
+ # @param [Kin::Nav::Menu] nav
16
+ # An instance of a Menu to be rendered as HTML.
17
+ # @param [#controller_name, #action_name] controller
18
+ # An object which behaves like a controller.
19
+ # @param [Hash] options
20
+ # A hash of options for customising the menu. See
21
+ # Kin::Nav::Helper#display_navigation.
22
+ #
23
+ # @api public
24
+ #
25
+ def initialize(nav, controller, options)
26
+ @nav = nav
27
+ @current = nav.active_item(controller)
28
+
29
+ @resource = options[:resource]
30
+ @inject = options.fetch(:inject, {})
31
+ @guards = options.fetch(:guard, {})
32
+
33
+ # Escape injected content.
34
+ @inject.each do |item_id, contents|
35
+ contents = Array(contents)
36
+ contents.map! { |v| Merb::Parse.escape_xml(v) }
37
+ @inject[item_id] = contents
38
+ end
38
39
  end
39
- end
40
40
 
41
- ##
42
- # Transforms the menu given to +initialize+ into a string containing
43
- # HTML.
44
- #
45
- # @return [String]
46
- # HTML containing the rendered menu. A collection of <li> elements
47
- # contained in a <ul>.
48
- #
49
- # @api public
50
- #
51
- def to_html
52
- '<ul>%s</ul>' % @nav.items.map do |item|
53
- trasform_item_to_html(item) if item.display?(@guards)
54
- end.join("\n")
55
- end
41
+ ##
42
+ # Transforms the menu given to +initialize+ into a string containing
43
+ # HTML.
44
+ #
45
+ # @return [String]
46
+ # HTML containing the rendered menu. A collection of <li> elements
47
+ # contained in a <ul>.
48
+ #
49
+ # @api public
50
+ #
51
+ def to_html
52
+ '<ul>%s</ul>' % @nav.items.map do |item|
53
+ trasform_item_to_html(item) if item.display?(@guards)
54
+ end.join("\n")
55
+ end
56
56
 
57
- private # ==============================================================
57
+ private # ==============================================================
58
58
 
59
- ##
60
- # Receives an individual nav item and transforms it into HTML.
61
- #
62
- # @param [Kin::Nav::Item] item
63
- # The nav item to be transformed.
64
- #
65
- # @return [String]
66
- # A string containing an HTML <li> element.
67
- #
68
- # @api private
69
- #
70
- def trasform_item_to_html(item)
71
- html_list_item(item) do
72
- html_link(item) { item.label(@inject[item.id]) }
59
+ ##
60
+ # Receives an individual nav item and transforms it into HTML.
61
+ #
62
+ # @param [Kin::Nav::Item] item
63
+ # The nav item to be transformed.
64
+ #
65
+ # @return [String]
66
+ # A string containing an HTML <li> element.
67
+ #
68
+ # @api private
69
+ #
70
+ def trasform_item_to_html(item)
71
+ html_list_item(item) do
72
+ html_link(item) { item.label(@inject[item.id]) }
73
+ end
73
74
  end
74
- end
75
+
76
+ ##
77
+ # Returns a list element, yielding to allow injection of the <a>
78
+ # element.
79
+ #
80
+ # @param [Kin::Nav::Item] item
81
+ # The item to be transformed to HTML.
82
+ #
83
+ # @return [String]
84
+ #
85
+ # @api private
86
+ #
87
+ def html_list_item(item)
88
+ opts =
89
+ case item.id == @current
90
+ when true then { :id => "nav_#{item.id}", :class => 'active' }
91
+ when false then { :id => "nav_#{item.id}" }
92
+ end
93
+
94
+ tag(:li, yield, opts)
95
+ end
96
+
97
+ ##
98
+ # Returns an anchor element containing a link for the menu item.
99
+ #
100
+ # @param [Kin::Nav::Item] item
101
+ # The item to be transformed to HTML.
102
+ #
103
+ # @return [String]
104
+ #
105
+ # @api private
106
+ #
107
+ def html_link(item)
108
+ tag(:a, yield,
109
+ :href => item.url(@resource),
110
+ :title => item.title(@inject[item.id])
111
+ )
112
+ end
113
+ end # Basic
75
114
 
76
115
  ##
77
- # Returns a list element, yielding to allow injection of the <a>
78
- # element.
116
+ # A formatter used when a navigation menu may has items which should
117
+ # appear on the right of the UI. Right-floated items are styled slightly
118
+ # differently.
79
119
  #
80
- # @param [Kin::Nav::Item] item
81
- # The item to be transformed to HTML.
82
- #
83
- # @return [String]
84
- #
85
- # @api private
86
- #
87
- def html_list_item(item)
88
- opts =
89
- case item.id == @current
90
- when true then { :id => "nav_#{item.id}", :class => 'active' }
91
- when false then { :id => "nav_#{item.id}" }
92
- end
120
+ class HasRight < Basic
121
+ class_inheritable_accessor :right_items
93
122
 
94
- tag(:li, yield, opts)
95
- end
123
+ private
124
+
125
+ ##
126
+ # Receives an individual nav item and transforms it into HTML.
127
+ #
128
+ # Items specified as being a right_item will be styled differently
129
+ # to the others.
130
+ #
131
+ # @param [Kin::Nav::Item] item
132
+ # The nav item to be transformed.
133
+ #
134
+ # @return [String]
135
+ # A string containing an HTML <li> element.
136
+ #
137
+ # @api private
138
+ #
139
+ def trasform_item_to_html(item)
140
+ return super unless self.class.right_items.include?(item.id)
141
+
142
+ html_list_item(item) do
143
+ link = html_link(item) { item.label(@inject[item.id]) }
144
+
145
+ <<-HTML
146
+ <span class="right_border">&nbsp;</span>
147
+ <span class="bg">#{ link }</span>
148
+ HTML
149
+ end
150
+ end # transform_item_to_html
151
+ end # HasRight
96
152
 
97
153
  ##
98
- # Returns an anchor element containing a link for the menu item.
154
+ # Creates a HasRight subclass.
155
+ #
156
+ # @param [Symbol] *items
157
+ # Symbols matching the given items which should have the custom style
158
+ # applied.
99
159
  #
100
- # @param [Kin::Nav::Item] item
101
- # The item to be transformed to HTML.
160
+ # @return [Class]
161
+ # A subclass of the HasRight formatter.
102
162
  #
103
- # @return [String]
163
+ # @example
164
+ # :formatter => Kin::Nav::Formatters::HasRight(:user, :guest)
104
165
  #
105
- # @api private
166
+ # @api public
106
167
  #
107
- def html_link(item)
108
- tag(:a, yield,
109
- :href => item.url(@resource),
110
- :title => item.title(@inject[item.id])
111
- )
168
+ def self.HasRight(*items)
169
+ klass = Class.new(Kin::Nav::Formatters::HasRight)
170
+ klass.right_items = Set.new(items)
171
+ klass
112
172
  end
113
- end # Formatter
114
173
 
174
+ ##
175
+ # A formatter used for creating subnavs.
176
+ #
177
+ class Subnav < Basic
178
+ private
179
+
180
+ ##
181
+ # Receives an individual nav item and transforms it into HTML.
182
+ #
183
+ # Wraps the contents of the link in a span.pill and span.icon to
184
+ # enable futher styling.
185
+ #
186
+ # @param [Kin::Nav::Item] item
187
+ # The nav item to be transformed.
188
+ #
189
+ # @return [String]
190
+ # A string containing an HTML <li> element.
191
+ #
192
+ # @api private
193
+ #
194
+ def trasform_item_to_html(item)
195
+ html_list_item(item) do
196
+ html_link(item) do
197
+ <<-HTML
198
+ <span class="pill">
199
+ <span class="icon">
200
+ #{item.label(@inject[item.id])}
201
+ </span>
202
+ </span>
203
+ HTML
204
+ end
205
+ end
206
+ end # transform_item_to_html
207
+ end # Subnav
208
+
209
+ end # Formatters
115
210
  end # Nav
116
211
  end # Kin