aaf-lipstick 1.1.0 → 2.0.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.
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+ module Lipstick
3
+ module Helpers
4
+ class FormValidationBuilder
5
+ def initialize(sym)
6
+ @rules = {}
7
+ @messages = {}
8
+ @sym = sym
9
+ end
10
+
11
+ def to_h
12
+ { rules: @rules, messages: @messages }
13
+ end
14
+
15
+ def validate_field(field, opts)
16
+ target = wrap_name(field)
17
+
18
+ opts.each do |k, v|
19
+ next validate_with_hash(target, k, v) if v.is_a?(Hash)
20
+ validate_with_param(target, k, v)
21
+ end
22
+ end
23
+
24
+ def auto_validate(obj, *fields)
25
+ unless obj.class.respond_to?(:lipstick_auto_validators)
26
+ raise("#{obj.class.name} does not include Lipstick::AutoValidation")
27
+ end
28
+
29
+ validators = obj.class.lipstick_auto_validators
30
+ validators.slice(*fields).each do |field, opts|
31
+ validate_field(field, opts)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def validate_with_hash(target, validation, opts)
38
+ message = opts.delete(:message)
39
+ add_message(target, validation, message) if message
40
+
41
+ return validate_with_param(target, validation, true) if opts.empty?
42
+
43
+ if opts.keys == [:param]
44
+ return validate_with_param(target, validation, opts[:param])
45
+ end
46
+
47
+ add_rule(target, validation, opts)
48
+ end
49
+
50
+ def validate_with_param(target, validation, param)
51
+ add_rule(target, validation, param)
52
+ end
53
+
54
+ def add_rule(target, key, value)
55
+ @rules[target] ||= {}
56
+ @rules[target][key] = value
57
+ end
58
+
59
+ def add_message(target, key, value)
60
+ @messages[target] ||= {}
61
+ @messages[target][key] = value
62
+ end
63
+
64
+ def wrap_name(name)
65
+ return name if @sym.nil?
66
+ "#{@sym}[#{name}]"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -1,23 +1,20 @@
1
- require 'action_view'
2
-
1
+ # frozen_string_literal: true
3
2
  module Lipstick
4
3
  module Helpers
5
4
  module LayoutHelper
6
- include ActionView::Helpers::TagHelper
7
- include ActionView::Helpers::TextHelper
8
- include ActionView::Helpers::CaptureHelper
9
-
10
- def aaf_header(title:, environment: nil, &bl)
5
+ def aaf_header(title:, environment: nil, auth: nil, &bl)
11
6
  content_tag('div', class: 'aaf-header') do
12
- concat(aaf_banner(title, environment))
7
+ concat(aaf_banner(title, environment, auth))
13
8
  concat(capture(&bl))
14
9
  end
15
10
  end
16
11
 
17
- def aaf_footer(&bl)
12
+ def aaf_footer
18
13
  content_tag('footer') do
19
- concat(content_tag('div', '', class: 'ui divider'))
20
- concat(capture(&bl))
14
+ content_tag('div', class: 'footer-content') do
15
+ concat(aaf_logo)
16
+ concat(capture { yield })
17
+ end
21
18
  end
22
19
  end
23
20
 
@@ -31,25 +28,23 @@ module Lipstick
31
28
  end
32
29
 
33
30
  def page_header(header, subheader = nil)
34
- content_tag('h2', class: 'ui header') do
35
- concat(header)
36
- if subheader
37
- concat(content_tag('div', subheader, class: 'sub header'))
31
+ content_tag('div', class: 'page-header') do
32
+ content_tag('h1') do
33
+ concat(header)
34
+ concat(' ')
35
+ concat(content_tag('small', subheader)) if subheader
38
36
  end
39
37
  end
40
38
  end
41
39
 
42
- def divider_tag
43
- content_tag('div', '', class: 'ui divider')
44
- end
45
-
46
40
  def yes_no_string(boolean)
47
41
  boolean ? 'Yes' : 'No'
48
42
  end
49
43
 
50
44
  def icon_tag(icon_class, html_opts = {})
51
- html_opts[:class] = "#{html_opts[:class]} icon #{icon_class}".strip
52
- content_tag('i', '', html_opts)
45
+ html_opts[:class] =
46
+ "#{html_opts[:class]} glyphicon glyphicon-#{icon_class}".strip
47
+ content_tag('span', '', html_opts)
53
48
  end
54
49
 
55
50
  # button_link_to(url_opts) { 'Link Text' }
@@ -60,77 +55,123 @@ module Lipstick
60
55
  args.unshift(capture(&block)) if block_given?
61
56
  text, url_opts, html_opts = args
62
57
  html_opts ||= {}
63
- html_opts[:class] = "#{html_opts[:class]} ui button".strip
58
+ html_opts[:class] ||= 'btn-default'
59
+ html_opts[:class] = "#{html_opts[:class]} btn".strip
64
60
  link_to(text, url_opts, html_opts)
65
61
  end
66
62
 
67
63
  def info_message(title, &block)
68
- message_block(title, 'info', 'info', &block)
64
+ alert_block(title, 'info', &block)
69
65
  end
70
66
 
71
67
  def error_message(title, &block)
72
- message_block(title, 'error', 'warning', &block)
68
+ alert_block(title, 'danger', &block)
73
69
  end
74
70
 
75
71
  def success_message(title, &block)
76
- message_block(title, 'success', 'smile', &block)
72
+ alert_block(title, 'success', &block)
77
73
  end
78
74
 
79
75
  def warning_message(title, &block)
80
- message_block(title, 'warning', 'warning', &block)
76
+ alert_block(title, 'warning', &block)
81
77
  end
82
78
 
83
79
  def breadcrumbs(*links)
84
- content_tag('div', class: 'ui breadcrumb') do
80
+ content_tag('ol', class: 'breadcrumb') do
85
81
  last = links.pop
86
82
 
87
83
  links.each do |link|
88
- concat(content_tag('div', breadcrumb_link(link), class: 'section'))
89
- concat(icon_tag('angle double right divider'))
84
+ concat(content_tag('li', breadcrumb_link(link)))
90
85
  end
91
86
 
92
- concat(content_tag('div', breadcrumb_link(last),
93
- class: 'active section'))
87
+ concat(content_tag('li', breadcrumb_link(last),
88
+ class: 'active'))
94
89
  end
95
90
  end
96
91
 
92
+ def will_paginate(_ = nil, options = {})
93
+ options[:renderer] ||= Lipstick::Helpers::PaginationLinkRenderer
94
+ super
95
+ end
96
+
97
+ DISABLE_ANIMATIONS_CSS = <<-EOF
98
+ .ui, .ui * {
99
+ -webkit-animation-duration: 0ms !important;
100
+ -moz-animation-duration: 0ms !important;
101
+ -ms-animation-duration: 0ms !important;
102
+ animation-duration: 0ms !important;
103
+ }
104
+ EOF
105
+ private_constant :DISABLE_ANIMATIONS_CSS
106
+
107
+ def disable_animations
108
+ content_tag('style', DISABLE_ANIMATIONS_CSS, type: 'text/css')
109
+ end
110
+
97
111
  private
98
112
 
99
- def aaf_banner(title, environment)
100
- content_tag('header', class: 'banner clearfix') do
101
- concat(aaf_environment_string(environment))
102
- header = content_tag('h2', class: 'ui inverted header') do
103
- # The logo width is also forced in CSS. The logo is scaled to 50% in
104
- # both width and height to improve quality on high-density displays.
105
- concat(tag('img', src: image_path('logo.png'), class: 'logo',
106
- width: 138, height: 80))
107
- concat(content_tag('div', title, class: 'content'))
113
+ def aaf_banner(title, environment, auth)
114
+ capture do
115
+ text = content_tag('header') do
116
+ concat(aaf_links(auth))
117
+ concat(title)
118
+ concat(aaf_environment_string(environment))
108
119
  end
109
- concat(header)
120
+ concat(text)
110
121
  end
111
122
  end
112
123
 
113
- def aaf_environment_string(environment)
114
- return if environment.nil?
115
- content_tag('div', environment, class: 'environment')
124
+ def aaf_logo
125
+ content_tag('span', '', class: 'pull-right logo hidden-xs hidden-sm')
116
126
  end
117
127
 
118
- def image_path(*)
119
- unless defined?(super)
120
- fail('No image_path method was found. This is typically provided by' \
121
- ' Rails or Sprockets::Helpers')
128
+ def aaf_links(auth)
129
+ content_tag('div', class: 'aaf-links pull-right') do
130
+ concat(aaf_home_link)
131
+ concat(aaf_support_link)
132
+ concat(aaf_auth_link(auth))
122
133
  end
123
- super
124
134
  end
125
135
 
126
- def message_block(title, color_class, icon_class, &block)
127
- content_tag('div', class: "ui icon message #{color_class}") do
128
- concat(icon_tag(icon_class))
129
- inner = content_tag('div', class: 'content') do
130
- concat(content_tag('div', title, class: 'header'))
131
- concat(capture(&block))
132
- end
133
- concat(inner)
136
+ def aaf_home_link
137
+ content_tag('a', href: 'https://aaf.edu.au') do
138
+ concat(icon_tag('aaf'))
139
+ concat(' AAF Home')
140
+ end
141
+ end
142
+
143
+ def aaf_support_link
144
+ content_tag('a', href: 'https://support.aaf.edu.au') do
145
+ concat(icon_tag('user'))
146
+ concat(' Support')
147
+ end
148
+ end
149
+
150
+ def aaf_auth_link(auth)
151
+ return if auth.nil?
152
+
153
+ text = ' Log In'
154
+ text = ' Log Out' if auth == :logout
155
+
156
+ icon = 'log-in'
157
+ icon = 'log-out' if auth == :logout
158
+
159
+ content_tag('a', href: "/auth/#{auth}") do
160
+ concat(icon_tag(icon))
161
+ concat(text)
162
+ end
163
+ end
164
+
165
+ def aaf_environment_string(environment)
166
+ return unless environment&.present?
167
+ content_tag('span', environment, class: 'environment')
168
+ end
169
+
170
+ def alert_block(title, color_class, &block)
171
+ opts = { class: "alert alert-#{color_class}", role: 'alert' }
172
+ content_tag('div', opts) do
173
+ concat(content_tag('h4', title))
174
+ concat(capture(&block))
134
175
  end
135
176
  end
136
177
 
@@ -1,48 +1,43 @@
1
+ # frozen_string_literal: true
1
2
  module Lipstick
2
3
  module Helpers
3
4
  module NavHelper
4
- include ActionView::Helpers::TagHelper
5
- include ActionView::Helpers::TextHelper
5
+ def nav_bar
6
+ content_tag('nav', class: 'navbar shrink') { yield }
7
+ end
6
8
 
7
- def nav_bar(&block)
8
- content_tag('nav') do
9
- css_class = 'ui borderless inverted menu collapsing nav parent'
10
- content_tag('div', class: css_class) do
11
- concat(nav_collapse_button)
12
- concat(capture(&block))
13
- end
9
+ def nav_first_item(text, url)
10
+ content_tag('div', class: 'nav navbar-header') do
11
+ concat(nav_collapse_button)
12
+ concat(content_tag('a', text, href: url, class: 'navbar-brand'))
14
13
  end
15
14
  end
16
15
 
17
- def nav_collapsing_items(&block)
18
- content_tag('span', class: 'collapsing nav content', &block)
16
+ def nav_collapsing_items
17
+ attrs = { class: 'collapse navbar-collapse', id: 'aaf-nav-collapse' }
18
+ content_tag('div', attrs) do
19
+ content_tag('ul', class: 'nav navbar-nav') { yield }
20
+ end
19
21
  end
20
22
 
21
23
  def nav_item(text, url, html_opts = {})
22
- html_opts[:class] = "#{html_opts[:class]} item".strip
23
- content_tag('a', text, html_opts.merge(href: url))
24
- end
25
-
26
- def nav_items_right(&block)
27
- content_tag('div', class: 'right menu', &block)
28
- end
29
-
30
- def nav_dropdown(title, &block)
31
- content_tag('div', class: 'ui simple dropdown item') do
32
- concat(content_tag('i', '', class: 'dropdown icon'))
33
- concat(title)
34
- concat(content_tag('div', class: 'menu', &block))
24
+ content_tag('li') do
25
+ content_tag('a', text, html_opts.merge(href: url))
35
26
  end
36
27
  end
37
28
 
38
29
  private
39
30
 
40
31
  def nav_collapse_button
41
- content_tag('div', class: 'collapsing nav button') do
42
- content_tag('a', href: '#',
43
- class: 'ui icon mini black open button') do
44
- content_tag('i', '', class: 'list layout icon')
45
- end
32
+ attrs = {
33
+ type: 'button', class: 'navbar-toggle collapsed',
34
+ 'data-toggle': 'collapse', 'data-target': '#aaf-nav-collapse',
35
+ 'aria-expanded': 'false'
36
+ }
37
+
38
+ content_tag('button', attrs) do
39
+ concat(content_tag('span', 'Toggle navigation', class: 'sr-only'))
40
+ 3.times { concat(content_tag('span', '', class: 'icon-bar')) }
46
41
  end
47
42
  end
48
43
  end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+ require 'will_paginate'
3
+
4
+ module Lipstick
5
+ module Helpers
6
+ class PaginationLinkRenderer
7
+ attr_reader :items, :current, :total, :template
8
+ private :current, :total, :template
9
+
10
+ def prepare(collection, _options, template)
11
+ @current = collection.current_page
12
+ @total = collection.total_pages
13
+
14
+ @items = [*prefix, *middle, *suffix]
15
+ @template = template
16
+ end
17
+
18
+ def to_html
19
+ template.content_tag('nav', class: 'pagination-wrapper') do
20
+ template.content_tag('ul', class: 'pagination') do
21
+ items.each do |item|
22
+ template.concat(render_item(item))
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def prefix
31
+ return [:prev] if !show_first? || short_list?
32
+ return [:prev, 1, :gap] if prefix_gap?
33
+ [:prev, 1]
34
+ end
35
+
36
+ def middle
37
+ return page_range(1, total) if short_list?
38
+ page_range(*page_window)
39
+ end
40
+
41
+ # Decides which pages to show in the links surrounding the "current" page.
42
+ def page_window
43
+ # If we're close to the start/end we can show a few extra page links.
44
+ return [1, 8] if current < 5
45
+ return [total - 7, total] if current > total - 4
46
+
47
+ # Otherwise show 3 pages either side of the current page
48
+ [current - 3, current + 3]
49
+ end
50
+
51
+ def suffix
52
+ return [:next] if !show_last? || short_list?
53
+ return [:gap, total, :next] if suffix_gap?
54
+ [total, :next]
55
+ end
56
+
57
+ def page_range(start, finish)
58
+ [start, 1].max.upto([finish, total].min)
59
+ end
60
+
61
+ def short_list?
62
+ total <= 10
63
+ end
64
+
65
+ def show_first?
66
+ !middle.include?(1)
67
+ end
68
+
69
+ def show_last?
70
+ !middle.include?(total)
71
+ end
72
+
73
+ def prefix_gap?
74
+ !middle.include?(2)
75
+ end
76
+
77
+ def suffix_gap?
78
+ !middle.include?(total - 1)
79
+ end
80
+
81
+ def render_item(item)
82
+ case item
83
+ when :prev
84
+ prev_item
85
+ when :next
86
+ next_item
87
+ when :gap
88
+ gap_item
89
+ else
90
+ link_to_page(item, page: item)
91
+ end
92
+ end
93
+
94
+ def prev_item
95
+ icon = template.content_tag('span', "\u25C0", 'aria-hidden': 'true')
96
+
97
+ return disabled_item(icon) if current < 2
98
+ link_to_page(icon, page: current - 1, 'aria-label': 'Previous')
99
+ end
100
+
101
+ def next_item
102
+ icon = template.content_tag('span', "\u25B6", 'aria-hidden': 'true')
103
+
104
+ return disabled_item(icon) if current >= total
105
+ link_to_page(icon, page: current + 1, 'aria-label': 'Next')
106
+ end
107
+
108
+ def gap_item
109
+ template.content_tag('li') do
110
+ template.content_tag('span', "\u2026", 'aria-hidden': 'true')
111
+ end
112
+ end
113
+
114
+ def link_to_page(content, page:, **opts)
115
+ li_opts = {}
116
+ li_opts[:class] = 'active' if page == current
117
+
118
+ url_opts = template.params.merge(page: page)
119
+ opts = opts.merge(href: template.url_for(url_opts))
120
+
121
+ template.content_tag('li', li_opts) do
122
+ template.content_tag('a', content, opts)
123
+ end
124
+ end
125
+
126
+ def disabled_item(content)
127
+ template.content_tag('li', content, class: 'disabled')
128
+ end
129
+ end
130
+ end
131
+ end