antw-kin 0.3.2 → 0.3.3

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.
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