roda-tags 0.1.1 → 0.2.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 +5 -5
- data/.github/dependabot.yml +24 -0
- data/.github/workflows/ruby.yml +45 -0
- data/.gitignore +1 -1
- data/.rubocop.yml +33 -0
- data/.rubocop_todo.yml +7 -0
- data/CODE_OF_CONDUCT.md +22 -16
- data/Gemfile +86 -0
- data/Guardfile +25 -0
- data/README.md +234 -236
- data/Rakefile +14 -11
- data/lib/core_ext/blank.rb +37 -36
- data/lib/core_ext/hash.rb +39 -16
- data/lib/core_ext/object.rb +2 -2
- data/lib/core_ext/string.rb +290 -116
- data/lib/roda/plugins/tag_helpers.rb +795 -575
- data/lib/roda/plugins/tags.rb +532 -276
- data/lib/roda/tags/version.rb +2 -3
- data/lib/roda/tags.rb +2 -0
- data/roda-tags.gemspec +30 -32
- metadata +44 -128
- data/.travis.yml +0 -4
@@ -1,82 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'roda'
|
2
4
|
require_relative '../../core_ext/string' unless ''.respond_to?(:titleize)
|
3
5
|
# require_relative '../../core_ext/object' unless :symbol.respond_to?(:in?)
|
4
6
|
|
5
7
|
class Roda
|
6
|
-
|
7
|
-
#
|
8
|
+
# add module documentation
|
8
9
|
module RodaPlugins
|
9
|
-
|
10
|
-
# TODO: add documentation here
|
10
|
+
# add module documentation
|
11
11
|
module RodaTagHelpers
|
12
12
|
# default options
|
13
13
|
OPTS = {
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
tags_label_append_str: ':',
|
18
|
-
# the default classes for various form tags. ie: shortcut to automatically add
|
14
|
+
tags_label_required_str: '<span>*</span>',
|
15
|
+
tags_label_append_str: ':',
|
16
|
+
# the default classes for various form tags. ie: shortcut to automatically add
|
19
17
|
# BS3 'form-control'
|
20
|
-
tags_forms_default_class:
|
21
|
-
|
18
|
+
tags_forms_default_class: '' # 'form-control',
|
19
|
+
|
22
20
|
}.freeze
|
23
|
-
|
24
|
-
|
21
|
+
|
25
22
|
# Depend on the render plugin, since this plugin only makes
|
26
23
|
# sense when the render plugin is used.
|
27
24
|
def self.load_dependencies(app, opts = OPTS)
|
28
25
|
app.plugin :tags, opts
|
29
26
|
end
|
30
|
-
|
27
|
+
|
28
|
+
# Configure the tag_helpers plugin with given options.
|
29
|
+
#
|
30
|
+
# @param app [Class] The Roda app class
|
31
|
+
# @param opts [Hash] Configuration options to merge with defaults
|
32
|
+
# If tag_helpers is already configured, merges with existing options
|
33
|
+
# Otherwise merges with OPTS constant defaults
|
34
|
+
#
|
35
|
+
# @return [void]
|
36
|
+
#
|
31
37
|
def self.configure(app, opts = {})
|
32
|
-
if app.opts[:tag_helpers]
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
+
opts = if app.opts[:tag_helpers]
|
39
|
+
app.opts[:tag_helpers][:orig_opts].merge(opts)
|
40
|
+
else
|
41
|
+
OPTS.merge(opts)
|
42
|
+
end
|
43
|
+
|
38
44
|
app.opts[:tag_helpers] = opts.dup
|
39
45
|
app.opts[:tag_helpers][:orig_opts] = opts
|
40
46
|
end
|
41
|
-
|
42
|
-
#
|
47
|
+
|
48
|
+
# add module documentation
|
43
49
|
module ClassMethods
|
44
50
|
# Return the uitags options for this class.
|
45
51
|
def tag_helpers_opts
|
46
52
|
opts[:tag_helpers]
|
47
53
|
end
|
48
|
-
|
49
54
|
end
|
50
|
-
|
51
|
-
#
|
55
|
+
|
56
|
+
# add module documentation
|
57
|
+
# rubocop:disable Metrics/ModuleLength
|
52
58
|
module InstanceMethods
|
53
|
-
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
59
|
+
# Constructs a form tag with given action and attributes
|
60
|
+
#
|
61
|
+
# @param action [String] The URL/path the form submits to
|
62
|
+
# @param attrs [Hash] HTML attributes for the form tag
|
63
|
+
# @param block [Block] Optional block containing form content
|
64
|
+
#
|
65
|
+
# @return [String] The generated HTML form tag and contents
|
66
|
+
#
|
67
|
+
# @example Basic usage
|
68
|
+
#
|
69
|
+
# form_tag('/register') do
|
70
|
+
# # form contents
|
60
71
|
# end
|
61
|
-
# #=>
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
# <% form_tag('/register', method: :put, id: 'register-form' ) %>
|
72
|
+
# #=> <form action="/register" method="post">
|
73
|
+
# # ...
|
74
|
+
# # </form>
|
75
|
+
#
|
76
|
+
# form_tag('/register', id: :register-form, class: 'form-control') do
|
68
77
|
# ...
|
69
|
-
#
|
70
|
-
# #=>
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
+
# end
|
79
|
+
# #=> <form action="/register" method="post" id="register-form" class="form-control">
|
80
|
+
# # ...
|
81
|
+
# # </form>
|
82
|
+
#
|
83
|
+
# @example Custom methods PUT/DELETE
|
84
|
+
#
|
85
|
+
# form_tag('/items', method: :put) do
|
86
|
+
# ...
|
87
|
+
# end
|
88
|
+
# #=> <form action="/items" method="post">
|
89
|
+
# # <input name="_method" type="hidden" value="PUT" />
|
90
|
+
# # </form>
|
91
|
+
#
|
92
|
+
# @example Multipart forms
|
93
|
+
#
|
78
94
|
# <% form_tag('/register', multipart: true ) %>
|
79
|
-
#
|
95
|
+
#
|
80
96
|
# <% form_tag('/register', multipart: 'multipart/form-data' ) %>
|
81
97
|
#
|
82
98
|
# <% form_tag('/register', enctype: 'multipart/form-data' ) %>
|
@@ -84,61 +100,72 @@ class Roda
|
|
84
100
|
# <form enctype="multipart/form-data" method="post" action="/register">
|
85
101
|
# ...
|
86
102
|
# </form>
|
87
|
-
#
|
88
|
-
def form_tag(action, attrs = {}, &block)
|
103
|
+
#
|
104
|
+
def form_tag(action, attrs = {}, &block)
|
89
105
|
attrs.reverse_merge!(method: :post, action: action)
|
90
106
|
method = attrs[:method]
|
107
|
+
|
91
108
|
# Unless the method is :get, fake out the method using :post
|
92
109
|
attrs[:method] = :post unless attrs[:method] == :get
|
93
|
-
faux_method_tag =
|
110
|
+
faux_method_tag = /post|get/.match?(method.to_s) ? '' : faux_method(method)
|
111
|
+
|
94
112
|
# set the enctype to multipart-form if we got a @multipart form
|
95
113
|
attrs[:enctype] = 'multipart/form-data' if attrs.delete(:multipart) || @multipart
|
96
114
|
captured_html = block_given? ? capture_html(&block) : ''
|
97
115
|
concat_content(tag(:form, faux_method_tag + captured_html, attrs))
|
98
116
|
end
|
99
|
-
|
117
|
+
|
100
118
|
# Constructs a label tag from the given options
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
119
|
+
#
|
120
|
+
# @example Basic usage
|
121
|
+
#
|
104
122
|
# <%= label_tag(:name) %>
|
105
123
|
# #=> <label for="name">Name:</label>
|
106
|
-
#
|
107
|
-
# Should accept a custom label text.
|
108
|
-
#
|
124
|
+
#
|
125
|
+
# @example Should accept a custom label text.
|
126
|
+
#
|
109
127
|
# <%= label_tag(:name, label: 'Custom label') %>
|
110
128
|
# #=> <label for="name">Custom label:</label>
|
111
|
-
#
|
112
|
-
# If label value is nil, then renders the default label text.
|
113
|
-
#
|
129
|
+
#
|
130
|
+
# @example If label value is nil, then renders the default label text.
|
131
|
+
#
|
114
132
|
# <%= label_tag(:name, label: nil) %>
|
115
133
|
# #=> <label for="name">Name:</label>
|
116
|
-
#
|
117
|
-
# Removes the label text when given :false.
|
118
|
-
#
|
134
|
+
#
|
135
|
+
# @example Removes the label text when given :false.
|
136
|
+
#
|
119
137
|
# <%= label_tag(:name, label: false) %>
|
120
138
|
# #=> <label for="name"></label>
|
121
|
-
#
|
122
|
-
# Appends the
|
123
|
-
#
|
139
|
+
#
|
140
|
+
# @example Appends the `app.forms_label_required_str` value, when the label is required.
|
141
|
+
#
|
124
142
|
# <%= label_tag(:name, required: true) %>
|
125
143
|
# #=> <label for="name">Name: <span>*</span></label>
|
126
|
-
#
|
127
|
-
|
128
|
-
|
129
|
-
|
144
|
+
#
|
145
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
146
|
+
def label_tag(field, attrs = {}, &block)
|
147
|
+
field = field.to_s
|
148
|
+
|
149
|
+
for_text = attrs.delete(:for)
|
150
|
+
# handle FALSE & nil values
|
151
|
+
for_text = field if for_text == false
|
152
|
+
for_text = field if for_text.nil?
|
153
|
+
|
154
|
+
attrs.reverse_merge!(label: field.titleize, for: for_text)
|
155
|
+
# attrs.reverse_merge!(label: field.titleize, for: field)
|
156
|
+
|
130
157
|
label_text = attrs.delete(:label)
|
131
158
|
# handle FALSE & nil values
|
132
159
|
label_text = '' if label_text == false
|
133
|
-
label_text = field.
|
134
|
-
|
160
|
+
label_text = field.titleize if label_text.nil?
|
161
|
+
|
135
162
|
unless label_text.to_s.empty?
|
136
163
|
label_text << opts_tag_helpers[:tags_label_append_str]
|
137
|
-
if attrs.delete(:required)
|
164
|
+
if attrs.delete(:required)
|
138
165
|
label_text = "#{label_text} #{opts_tag_helpers[:tags_label_required_str]}"
|
139
166
|
end
|
140
167
|
end
|
141
|
-
|
168
|
+
|
142
169
|
if block_given? # label with inner content
|
143
170
|
label_content = label_text + capture_html(&block)
|
144
171
|
concat_content(tag(:label, label_content, attrs))
|
@@ -146,211 +173,220 @@ class Roda
|
|
146
173
|
tag(:label, label_text, attrs)
|
147
174
|
end
|
148
175
|
end
|
149
|
-
|
176
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
177
|
+
|
150
178
|
# Constructs a hidden field input from the given options
|
151
179
|
#
|
152
|
-
#
|
153
|
-
#
|
180
|
+
# @param name [String] The field name used for :id & :name attributes
|
181
|
+
# @param attrs [Hash] HTML attributes for the input[@type=hidden] tag
|
182
|
+
#
|
183
|
+
# @return [String] The generated HTML input[@type=hidden] tag
|
184
|
+
#
|
185
|
+
# @example Basic usage
|
186
|
+
#
|
154
187
|
# <%= hidden_field_tag(:snippet_name) %>
|
155
188
|
# #=>
|
156
189
|
# <input id="snippet_name" name="snippet_name" type="hidden">
|
157
|
-
#
|
158
|
-
# Providing a value:
|
159
|
-
#
|
190
|
+
#
|
191
|
+
# @example Providing a value:
|
192
|
+
#
|
160
193
|
# <%= hidden_field_tag(:snippet_name, value: 'myvalue') %>
|
161
194
|
# #=>
|
162
195
|
# <input id="snippet_name" name="snippet_name" type="hidden" value="myvalue">
|
163
|
-
#
|
164
|
-
# Setting a different
|
165
|
-
#
|
196
|
+
#
|
197
|
+
# @example Setting a different `:id`
|
198
|
+
#
|
166
199
|
# <%= hidden_field_tag(:snippet_name, id: 'some-id') %>
|
167
200
|
# #=>
|
168
201
|
# <input id="some-id" name="snippet_name" type="hidden">
|
169
|
-
#
|
170
|
-
# Removing the
|
171
|
-
#
|
202
|
+
#
|
203
|
+
# @example Removing the `:id` attribute completely.
|
204
|
+
#
|
172
205
|
# <%= hidden_field_tag(:snippet_name, id: false ) %>
|
173
206
|
# #=>
|
174
207
|
# <input name="snippet_name" type="hidden">
|
175
|
-
#
|
176
|
-
def hidden_field_tag(name, attrs = {})
|
208
|
+
#
|
209
|
+
def hidden_field_tag(name, attrs = {})
|
210
|
+
name = name.to_s
|
211
|
+
attrs = {} if attrs.nil?
|
212
|
+
|
177
213
|
attrs.reverse_merge!(name: name, value: '', type: :hidden)
|
178
214
|
attrs = add_css_id(attrs, name)
|
179
215
|
tag(:input, attrs)
|
180
216
|
end
|
181
|
-
|
182
|
-
|
183
|
-
# Creates a standard text field; use these text fields to input smaller chunks of text like
|
217
|
+
alias hiddenfield_tag hidden_field_tag
|
218
|
+
|
219
|
+
# Creates a standard text field; use these text fields to input smaller chunks of text like
|
184
220
|
# a username or a search query.
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
221
|
+
#
|
222
|
+
# @param name [String] The field name used for :id & :name attributes
|
223
|
+
# @param attrs [Hash] HTML attributes for the input[@type=text] tag
|
224
|
+
#
|
225
|
+
# @return [String] The generated HTML input[@type=text] tag
|
226
|
+
#
|
227
|
+
# @example Basic usage
|
188
228
|
# text_field_tag(:snippet_name)
|
189
|
-
# #=>
|
190
|
-
#
|
191
|
-
#
|
192
|
-
# Providing a value:
|
193
|
-
#
|
229
|
+
# #=> <input class="text" id="snippet_name" name="snippet_name" type="text">
|
230
|
+
#
|
231
|
+
# @example With a value
|
194
232
|
# text_field_tag(:snippet, value: 'some-value')
|
195
|
-
# #=>
|
196
|
-
#
|
197
|
-
#
|
198
|
-
# Setting a different <tt>:id</tt>
|
199
|
-
#
|
233
|
+
# #=> <input class="text" id="snippet" name="snippet" type="text" value="some-value">
|
234
|
+
#
|
235
|
+
# @example Setting a different `:id`
|
200
236
|
# text_field_tag(:snippet_name, id: 'some-id')
|
201
|
-
# #=>
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
237
|
+
# #=> <input class="text" id="some-id" name="snippet_name" type="text">
|
238
|
+
#
|
239
|
+
# @example Removing the `:id` attribute completely.
|
240
|
+
# NOTE! bad practice.
|
241
|
+
#
|
206
242
|
# text_field_tag(:snippet_name, id: false)
|
207
|
-
# #=>
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
243
|
+
# #=> <input class="text" name="snippet_name" type="text">
|
244
|
+
#
|
245
|
+
# @example Adding another CSS class.
|
246
|
+
# NOTE! appends the the class to the default class `.text`.
|
247
|
+
#
|
212
248
|
# text_field_tag(:snippet_name, class: :big )
|
213
|
-
# #=>
|
214
|
-
#
|
215
|
-
#
|
216
|
-
# Adds a <tt>:title</tt> attribute when passed <tt>:ui_hint</tt>.
|
217
|
-
#
|
249
|
+
# #=> <input class="big text" id="snippet_name" name="snippet_name" type="text">
|
250
|
+
#
|
251
|
+
# @example Adds a `:title` attribute when passed `:ui_hint`.
|
218
252
|
# text_field_tag(:name, ui_hint: 'a user hint')
|
219
|
-
# #=>
|
220
|
-
#
|
221
|
-
#
|
222
|
-
# Supports <tt>:maxlength</tt> & <tt>:size</tt> attributes.
|
223
|
-
#
|
253
|
+
# #=> <input class="text" id="name" name="name" title="a user hint" type="text">
|
254
|
+
#
|
255
|
+
# @example Supports `:maxlength` & `:size` attributes.
|
224
256
|
# text_field_tag(:ip, maxlength: 15, size: 20)
|
225
|
-
# #=>
|
226
|
-
#
|
227
|
-
#
|
228
|
-
# Supports <tt>:disabled</tt> & <tt>:readonly</tt> attributes.
|
229
|
-
#
|
257
|
+
# #=> <input class="text" id="ip" maxlength="15" name="ip" size="20" type="text">
|
258
|
+
#
|
259
|
+
# @example Supports `:disabled` & `:readonly` attributes.
|
230
260
|
# text_field_tag(:name, disabled: true)
|
231
|
-
# #=>
|
232
|
-
#
|
233
|
-
#
|
261
|
+
# #=> <input class="text" disabled="disabled" id="name" name="name" type="text" >
|
262
|
+
#
|
234
263
|
# text_field_tag(:name, readonly: true)
|
235
|
-
# #=>
|
236
|
-
# <input class="text" id="name" name="name" readonly="readonly" type="text">
|
237
|
-
#
|
264
|
+
# #=> <input class="text" id="name" name="name" readonly="readonly" type="text">
|
238
265
|
#
|
239
|
-
def text_field_tag(name, attrs = {})
|
266
|
+
def text_field_tag(name, attrs = {})
|
267
|
+
name = name.to_s
|
268
|
+
attrs = {} if attrs.nil?
|
269
|
+
|
240
270
|
attrs.reverse_merge!(name: name, type: :text)
|
241
271
|
attrs = add_css_id(attrs, name)
|
242
272
|
attrs = add_css_class(attrs, :text)
|
243
273
|
attrs = add_ui_hint(attrs)
|
244
274
|
tag(:input, attrs)
|
245
275
|
end
|
246
|
-
|
247
|
-
|
276
|
+
alias textfield_tag text_field_tag
|
277
|
+
|
248
278
|
# Constructs a password field input from the given options
|
249
|
-
#
|
250
|
-
#
|
251
|
-
#
|
279
|
+
#
|
280
|
+
# @param name [String] The field name used for :id & :name attributes
|
281
|
+
# @param attrs [Hash] HTML attributes for the input[@type=password] tag
|
282
|
+
#
|
283
|
+
# @return [String] The generated HTML input[@type=password] tag
|
284
|
+
#
|
285
|
+
# @example Basic usage
|
252
286
|
# password_field_tag(:snippet_name)
|
253
287
|
# #=> <input class="text" id="snippet_name" name="snippet_name" type="password">
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
288
|
+
#
|
289
|
+
# @example With a value
|
257
290
|
# password_field_tag(:snippet_name, value: 'some-value')
|
258
291
|
# #=>
|
259
292
|
# <input class="text" id="snippet" name="snippet" type="password" value="some-value">
|
260
|
-
#
|
261
|
-
#
|
262
|
-
#
|
293
|
+
#
|
294
|
+
# @example With custom `:id` attribute
|
263
295
|
# password_field_tag(:snippet_name, id: 'some-id')
|
264
296
|
# #=> <input class="text" id="some-id" name="snippet_name" type="password">
|
265
|
-
#
|
266
|
-
#
|
267
|
-
#
|
297
|
+
#
|
298
|
+
# @example Without the `:id` attribute
|
299
|
+
# NOTE! bad practice.
|
300
|
+
#
|
268
301
|
# password_field_tag(:snippet_name, id: false)
|
269
302
|
# #=> <input class="text" name="snippet_name" type="password">
|
270
|
-
#
|
271
|
-
# Adding another CSS class
|
272
|
-
#
|
303
|
+
#
|
304
|
+
# @example Adding another CSS class
|
305
|
+
# NOTE! appends the the class to the default class `.text`.
|
306
|
+
#
|
273
307
|
# password_field_tag(:snippet_name, class: :big )
|
274
308
|
# #=> <input class="big text" id="snippet_name" name="snippet_name" type="password">
|
275
|
-
#
|
276
|
-
# Adds a
|
277
|
-
#
|
309
|
+
#
|
310
|
+
# @example Adds a `:title` attribute when passed `:ui_hint`.
|
278
311
|
# password_field_tag(:name, ui_hint: 'a user hint')
|
279
312
|
# #=> <input class="text" id="name" name="name" title="a user hint" type="password">
|
280
|
-
#
|
281
|
-
# Supports
|
282
|
-
#
|
313
|
+
#
|
314
|
+
# @example Supports `:maxlength`, `:size` & `:disabled` attributes
|
283
315
|
# password_field_tag(:ip, maxlength: 15, size: 20)
|
284
316
|
# #=> <input class="text" id="ip" maxlength="15" name="ip" size="20" type="password">
|
285
|
-
#
|
317
|
+
#
|
286
318
|
# password_field_tag(:name, disabled: true)
|
287
319
|
# #=> <input class="text" id="name" disabled="disabled" name="name" type="password">
|
288
|
-
#
|
289
|
-
def password_field_tag(name, attrs = {})
|
320
|
+
#
|
321
|
+
def password_field_tag(name, attrs = {})
|
322
|
+
name = name.to_s
|
323
|
+
attrs = {} if attrs.nil?
|
324
|
+
|
290
325
|
attrs.reverse_merge!(name: name, type: :password)
|
291
326
|
attrs = add_css_id(attrs, name)
|
292
327
|
attrs = add_css_class(attrs, :text) # deliberately giving it the .text class
|
293
328
|
attrs = add_ui_hint(attrs)
|
294
329
|
tag(:input, attrs)
|
295
330
|
end
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
# Creates a file upload field. If you are using file uploads then you will also
|
331
|
+
alias passwordfield_tag password_field_tag
|
332
|
+
|
333
|
+
# Creates a file upload field. If you are using file uploads then you will also
|
300
334
|
# need to set the multipart option for the form tag:
|
301
|
-
#
|
335
|
+
#
|
302
336
|
# <% form_tag '/upload', :multipart => true do %>
|
303
337
|
# <label for="file">File to Upload</label>
|
304
|
-
# <%= file_field_tag
|
338
|
+
# <%= file_field_tag 'file' %>
|
305
339
|
# <%= submit_tag %>
|
306
340
|
# <% end %>
|
307
|
-
#
|
308
|
-
# The specified URL will then be passed a File object containing the selected file,
|
341
|
+
#
|
342
|
+
# The specified URL will then be passed a File object containing the selected file,
|
309
343
|
# or if the field was left blank, a StringIO object.
|
310
|
-
#
|
311
|
-
#
|
312
|
-
#
|
344
|
+
#
|
345
|
+
# @param name [String] The field name used for :id & :name attributes
|
346
|
+
# @param attrs [Hash] HTML attributes for the input[@type=file] tag
|
347
|
+
#
|
348
|
+
# @return [String] The generated HTML input[@type=file] tag
|
349
|
+
#
|
350
|
+
# @example Basic usage
|
313
351
|
# file_field_tag('attachment')
|
314
352
|
# #=> <input class="file" id="attachment" name="attachment" type="file">
|
315
|
-
#
|
316
|
-
# Ignores the invalid
|
317
|
-
#
|
353
|
+
#
|
354
|
+
# @example Ignores the invalid `:value` attribute.
|
318
355
|
# file_field_tag(:photo, value: 'some-value')
|
319
356
|
# #=> <input class="file" id="photo" name="photo" type="file">
|
320
|
-
#
|
321
|
-
# Setting a different
|
322
|
-
#
|
357
|
+
#
|
358
|
+
# @example Setting a different `:id`
|
323
359
|
# file_field_tag(:photo, id: 'some-id')
|
324
360
|
# #=> <input class="file" id="some-id" name="photo" type="file">
|
325
|
-
#
|
326
|
-
# Removing the
|
327
|
-
#
|
361
|
+
#
|
362
|
+
# @example Removing the `:id` attribute completely
|
363
|
+
# NOTE! bad practice.
|
364
|
+
#
|
328
365
|
# file_field_tag(:photo, id: false)
|
329
366
|
# #=> <input class="file" name="photo" type="file">
|
330
|
-
#
|
331
|
-
# Adding another CSS class.
|
332
|
-
#
|
367
|
+
#
|
368
|
+
# @example Adding another CSS class.
|
369
|
+
# NOTE! appends the the class to the default class +.text+.
|
370
|
+
#
|
333
371
|
# file_field_tag(:photo, class: :big )
|
334
372
|
# #=> <input class="big file" id="photo" name="photo" type="file">
|
335
|
-
#
|
336
|
-
# Adds a
|
337
|
-
#
|
373
|
+
#
|
374
|
+
# @example Adds a `:title` attribute when passed `:ui_hint`. Also works with +:title+.
|
338
375
|
# file_field_tag(:photo, ui_hint: 'a user hint')
|
339
376
|
# #=> <input class="file" id="photo" name="photo" title="a user hint" type="file">
|
340
|
-
#
|
341
|
-
# Supports the
|
342
|
-
#
|
377
|
+
#
|
378
|
+
# @example Supports the `:disabled` attribute.
|
343
379
|
# file_field_tag(:photo, disabled: true)
|
344
380
|
# #=> <input class="file" disabled="disabled" id="photo" name="photo" type="file">
|
345
|
-
#
|
346
|
-
#
|
347
|
-
# Supports the <tt>:accept</tt> attribute, even though most browsers don't.
|
348
|
-
#
|
381
|
+
#
|
382
|
+
# @example Supports the `:accept` attribute, even though most browsers don't.
|
349
383
|
# file_field_tag(:photo, accept: 'image/png,image/jpeg')
|
350
|
-
# #=>
|
351
|
-
#
|
352
|
-
|
353
|
-
|
384
|
+
# #=> <input accept="image/png,image/jpeg" class="file" ... type="file">
|
385
|
+
#
|
386
|
+
def file_field_tag(name, attrs = {})
|
387
|
+
name = name.to_s
|
388
|
+
attrs = {} if attrs.nil?
|
389
|
+
|
354
390
|
attrs.reverse_merge!(name: name, type: :file)
|
355
391
|
attrs.delete(:value) # can't use value, so delete it if present
|
356
392
|
attrs = add_css_id(attrs, name)
|
@@ -358,240 +394,267 @@ class Roda
|
|
358
394
|
attrs = add_ui_hint(attrs)
|
359
395
|
tag(:input, attrs)
|
360
396
|
end
|
361
|
-
|
362
|
-
|
363
|
-
|
397
|
+
alias filefield_tag file_field_tag
|
398
|
+
|
364
399
|
# Constructs a textarea input from the given options
|
365
|
-
#
|
366
|
-
#
|
367
|
-
#
|
368
|
-
# * <tt>:escape</tt> - By default, the contents of the text input are HTML escaped. If you
|
400
|
+
#
|
401
|
+
# * `:escape` - By default, the contents of the text input are HTML escaped. If you
|
369
402
|
# need unescaped contents, set this to false.
|
370
|
-
#
|
403
|
+
#
|
371
404
|
# Any other key creates standard HTML attributes for the tag.
|
372
405
|
#
|
373
|
-
#
|
374
|
-
#
|
406
|
+
# @param name [String] The field name used for :id & :name attributes
|
407
|
+
# @param attrs [Hash] HTML attributes for the textarea tag
|
408
|
+
#
|
409
|
+
# @return [String] The generated HTML textarea tag
|
410
|
+
#
|
411
|
+
# TODO: enable :escape functionality...
|
412
|
+
#
|
413
|
+
# @example Basic usage
|
375
414
|
# textarea_tag('post')
|
376
415
|
# #=> <textarea id="post" name="post">\n</textarea>
|
377
|
-
#
|
378
|
-
# Providing a value:
|
379
|
-
#
|
416
|
+
#
|
417
|
+
# @example Providing a value:
|
380
418
|
# textarea_tag(:bio, value: @actor.bio)
|
381
419
|
# #=> <textarea id="bio" name="bio">This is my biography.\n</textarea>
|
382
|
-
#
|
383
|
-
# Setting a different
|
384
|
-
#
|
420
|
+
#
|
421
|
+
# @example Setting a different `:id`
|
385
422
|
# textarea_tag(:body, id: 'some-id')
|
386
423
|
# #=> <textarea id="some-id" name="post">\n\n</textarea>
|
387
|
-
#
|
388
|
-
# Adding a CSS class.
|
389
|
-
#
|
424
|
+
#
|
425
|
+
# @example Adding a CSS class.
|
426
|
+
# NOTE! textarea has no other class by default.
|
427
|
+
#
|
390
428
|
# textarea_tag(:body, class: 'big')
|
391
429
|
# #=> <textarea class="big" id="post" name="post">\n</textarea>
|
392
|
-
#
|
393
|
-
# Adds a
|
394
|
-
#
|
430
|
+
#
|
431
|
+
# @example Adds a `:title` attribute when passed `:ui_hint`.
|
395
432
|
# textarea_tag(:body, ui_hint: 'a user hint')
|
396
433
|
# #=> <textarea id="post" name="post" title="a user hint">\n</textarea>
|
397
|
-
#
|
398
|
-
# Supports
|
399
|
-
#
|
434
|
+
#
|
435
|
+
# @example Supports `:rows` & `:cols` attributes.
|
400
436
|
# textarea_tag('body', rows: 10, cols: 25)
|
401
437
|
# #=> <textarea cols="25" id="body" name="body" rows="10">\n</textarea>
|
402
|
-
#
|
403
|
-
# Supports shortcut to
|
404
|
-
#
|
438
|
+
#
|
439
|
+
# @example Supports shortcut to `:rows` & `:cols` attributes, via the `:size` attribute.
|
405
440
|
# textarea_tag( 'body', size: "25x10")
|
406
441
|
# #=> <textarea cols="25" id="body" name="body" rows="10"></textarea>
|
407
|
-
#
|
408
|
-
# Supports
|
409
|
-
#
|
442
|
+
#
|
443
|
+
# @example Supports `:disabled` & `:readonly` attributes.
|
410
444
|
# textarea_tag(:description, disabled: true)
|
411
445
|
# #=> <textarea disabled="disabled" id="description" name="description"></textarea>
|
412
|
-
#
|
446
|
+
#
|
413
447
|
# textarea_tag(:description, readonly: true)
|
414
448
|
# #=> <textarea id="description" name="description" readonly="readonly"></textarea>
|
415
|
-
#
|
416
|
-
def textarea_tag(name, attrs = {})
|
449
|
+
#
|
450
|
+
def textarea_tag(name, attrs = {})
|
451
|
+
name = name.to_s
|
452
|
+
attrs = {} if attrs.nil?
|
453
|
+
|
417
454
|
attrs.reverse_merge!(name: name)
|
418
455
|
attrs = add_css_id(attrs, name)
|
419
|
-
if size = attrs.delete(:size)
|
420
|
-
attrs[:cols], attrs[:rows] = size.split('x')
|
456
|
+
if (size = attrs.delete(:size)) && size.respond_to?(:split) && size.match?(/^\d+x\d+$/)
|
457
|
+
attrs[:cols], attrs[:rows] = size.split('x')
|
421
458
|
end
|
422
|
-
|
459
|
+
|
460
|
+
# TODO: add sanitation support of the value passed
|
461
|
+
# content = Rack::Utils.escape_html(attrs.delete(:value).to_s)
|
462
|
+
content = attrs.delete(:value).to_s
|
463
|
+
|
423
464
|
attrs = add_ui_hint(attrs)
|
424
465
|
tag(:textarea, content, attrs)
|
425
466
|
end
|
426
|
-
|
427
|
-
|
428
|
-
# Creates a
|
429
|
-
#
|
430
|
-
#
|
431
|
-
#
|
432
|
-
#
|
433
|
-
#
|
434
|
-
#
|
435
|
-
#
|
436
|
-
#
|
437
|
-
#
|
438
|
-
#
|
439
|
-
#
|
440
|
-
#
|
441
|
-
#
|
467
|
+
alias text_area_tag textarea_tag
|
468
|
+
|
469
|
+
# Creates a fieldset tag wrapping the provided content
|
470
|
+
#
|
471
|
+
# @param args [Array] Arguments - first arg can be legend text or last arg can be attrs hash
|
472
|
+
# @param attrs [Hash] Optional HTML attributes hash if last arg is a hash
|
473
|
+
# @yield Optional block containing fieldset content
|
474
|
+
# @yieldreturn [String] The captured HTML content for the fieldset
|
475
|
+
#
|
476
|
+
# @return [String] The generated HTML fieldset with legend and content
|
477
|
+
#
|
478
|
+
# @example Basic usage with legend text
|
479
|
+
# fieldset_tag("User Details")
|
480
|
+
# #=> <fieldset id="fieldset-user-details">
|
481
|
+
# # <legend>User Details</legend>
|
482
|
+
# # </fieldset>
|
483
|
+
#
|
484
|
+
# @example With attributes and block content
|
485
|
+
# fieldset_tag("Details", class: "form-section") do
|
486
|
+
# text_field_tag(:name)
|
487
|
+
# end
|
488
|
+
# #=> <fieldset class="form-section" id="fieldset-details">
|
489
|
+
# # <legend>Details</legend>
|
490
|
+
# # <input type="text" name="name" id="name">
|
491
|
+
# # </fieldset>
|
492
|
+
#
|
493
|
+
# @example With legend in attributes
|
494
|
+
# fieldset_tag(legend: "Section", class: "bordered")
|
495
|
+
# #=> <fieldset class="bordered" id="fieldset">
|
496
|
+
# # <legend>Section</legend>
|
497
|
+
# # </fieldset>
|
498
|
+
#
|
499
|
+
# @example Sets the `<legend>` and `:id` attribute when given a single argument.
|
500
|
+
# <% fieldset_tag 'User Details' do %>
|
501
|
+
# <p><%= textfield_tag 'name' %></p>
|
442
502
|
# <% end %>
|
443
|
-
# #=>
|
444
|
-
#
|
445
|
-
#
|
446
|
-
#
|
447
|
-
#
|
448
|
-
#
|
449
|
-
#
|
450
|
-
#
|
451
|
-
#
|
452
|
-
#
|
453
|
-
#
|
454
|
-
#
|
455
|
-
#
|
456
|
-
#
|
457
|
-
#
|
458
|
-
#
|
459
|
-
#
|
460
|
-
#
|
461
|
-
#
|
462
|
-
#
|
463
|
-
#
|
464
|
-
#
|
465
|
-
#
|
466
|
-
#
|
467
|
-
#
|
468
|
-
#
|
469
|
-
#
|
470
|
-
#
|
471
|
-
#
|
472
|
-
#
|
473
|
-
#
|
474
|
-
|
475
|
-
# #=>
|
476
|
-
# <fieldset>
|
477
|
-
# <legend>User Details</legend>
|
478
|
-
# <snip...>
|
479
|
-
#
|
480
|
-
# @api public
|
481
|
-
def field_set_tag(*args, &block)
|
503
|
+
# #=> <fieldset id="fieldset-user-details">
|
504
|
+
# # <legend>User Details</legend>
|
505
|
+
# # <p><input name="name" class="text" id="name" type="text"></p>
|
506
|
+
# # </fieldset>
|
507
|
+
#
|
508
|
+
# @example Supports `:legend` attribute for the `<legend>` tag.
|
509
|
+
# fieldset_tag(:actor, legend: 'Your Details')
|
510
|
+
# #=> <fieldset id="fieldset-actor">
|
511
|
+
# # <legend>Your Details</legend>
|
512
|
+
# # <snip...>
|
513
|
+
#
|
514
|
+
# @example Adding a CSS class.
|
515
|
+
#
|
516
|
+
# NOTE! fieldset has no other class by default.
|
517
|
+
#
|
518
|
+
# fieldset_tag(:actor, class: "legend-class")
|
519
|
+
# #=> <fieldset class="legend-class" id="fieldset-actor">
|
520
|
+
# # <snip...>
|
521
|
+
#
|
522
|
+
# @example When passed +nil+ as the first argument the `:id` becomes 'fieldset'.
|
523
|
+
# fieldset_tag(nil, class: 'format')
|
524
|
+
# #=> <fieldset class="format" id="fieldset">
|
525
|
+
# # <snip...>
|
526
|
+
#
|
527
|
+
# @example Removing the `:id` attribute completely.
|
528
|
+
# fieldset_tag('User Details', id: false)
|
529
|
+
# #=> <fieldset>
|
530
|
+
# # <legend>User Details</legend>
|
531
|
+
# # <snip...>
|
532
|
+
#
|
533
|
+
# rubocop:disable Metrics/AbcSize
|
534
|
+
def fieldset_tag(*args, &block)
|
482
535
|
attrs = args.last.is_a?(Hash) ? args.pop : {}
|
483
536
|
attrs = add_css_id(attrs, ['fieldset', args.first].compact.join('-'))
|
537
|
+
|
484
538
|
legend_text = args.first.is_a?(String || Symbol) ? args.first : attrs.delete(:legend)
|
485
539
|
legend_html = legend_text.blank? ? '' : tag(:legend, legend_text)
|
486
540
|
captured_html = block_given? ? capture_html(&block) : ''
|
487
541
|
concat_content(tag(:fieldset, legend_html + captured_html, attrs))
|
488
542
|
end
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
#
|
493
|
-
#
|
494
|
-
#
|
543
|
+
# rubocop:enable Metrics/AbcSize
|
544
|
+
alias field_set_tag fieldset_tag
|
545
|
+
|
546
|
+
# Creates a legend tag with given contents and attributes
|
547
|
+
#
|
548
|
+
# @param contents [String] The text content for the legend
|
549
|
+
# @param attrs [Hash] HTML attributes for the legend tag
|
550
|
+
#
|
551
|
+
# @return [String] The generated HTML legend tag
|
552
|
+
#
|
553
|
+
# @example Basic usage
|
495
554
|
# legend_tag('User Details')
|
496
555
|
# #=> <legend>User Details</legend>
|
497
|
-
#
|
498
|
-
#
|
499
|
-
#
|
500
|
-
#
|
501
|
-
#
|
502
|
-
#
|
503
|
-
#
|
504
|
-
#
|
505
|
-
#
|
506
|
-
|
507
|
-
#
|
508
|
-
def legend_tag(contents, attrs = {})
|
556
|
+
#
|
557
|
+
# @example With id attribute
|
558
|
+
# legend_tag('User Details', id: 'user-legend')
|
559
|
+
# #=> <legend id="user-legend">User Details</legend>
|
560
|
+
#
|
561
|
+
# @example With CSS class
|
562
|
+
# legend_tag('User Details', class: 'form-legend')
|
563
|
+
# #=> <legend class="form-legend">User Details</legend>
|
564
|
+
#
|
565
|
+
def legend_tag(contents, attrs = {})
|
509
566
|
tag(:legend, contents, attrs)
|
510
567
|
end
|
511
|
-
|
568
|
+
|
512
569
|
# Creates a checkbox element.
|
513
|
-
#
|
514
|
-
#
|
515
|
-
#
|
570
|
+
#
|
571
|
+
# @param name [String] The field name used for :id & :name attributes
|
572
|
+
# @param attrs [Hash] HTML attributes for the input[@type=checkbox] tag
|
573
|
+
#
|
574
|
+
# @return [String] The generated HTML input[@type=checkbox] tag
|
575
|
+
#
|
576
|
+
# @example Basic usage
|
516
577
|
# check_box_tag(:accept)
|
517
578
|
# #=> <input class="checkbox" id="accept" name="accept" type="checkbox" value="1">
|
518
|
-
#
|
519
|
-
# Providing a value:
|
520
|
-
#
|
579
|
+
#
|
580
|
+
# @example Providing a value:
|
521
581
|
# check_box_tag(:rock, value: 'rock music')
|
522
582
|
# #=> <input class="checkbox" id="rock" name="rock" type="checkbox" value="rock music">
|
523
|
-
#
|
524
|
-
# Setting a different
|
525
|
-
#
|
583
|
+
#
|
584
|
+
# @example Setting a different `:id`.
|
526
585
|
# check_box_tag(:rock, :id => 'some-id')
|
527
586
|
# #=> <input class="checkbox" id="some-id" name="rock" type="checkbox" value="1">
|
528
|
-
#
|
529
|
-
# Adding another CSS class.
|
530
|
-
#
|
587
|
+
#
|
588
|
+
# @example Adding another CSS class.
|
589
|
+
# NOTE! appends the the class to the default class `.checkbox`.
|
590
|
+
#
|
531
591
|
# check_box_tag(:rock, class: 'small')
|
532
592
|
# #=> <input class="small checkbox" id="rock" name="rock" type="checkbox" value="1">
|
533
|
-
#
|
534
|
-
# Adds a
|
535
|
-
#
|
593
|
+
#
|
594
|
+
# @example Adds a `:title` attribute when passed `:ui_hint`.
|
536
595
|
# check_box_tag(:rock, ui_hint: 'a user hint')
|
537
596
|
# #=> <input ... title="a user hint" type="checkbox" value="1">
|
538
|
-
#
|
539
|
-
# Supports the
|
540
|
-
#
|
597
|
+
#
|
598
|
+
# @example Supports the `:disabled` & `:checked` attributes.
|
541
599
|
# check_box_tag(:rock, checked: true)
|
542
600
|
# #=> <input checked="checked" ... type="checkbox" value="1">
|
543
|
-
#
|
601
|
+
#
|
544
602
|
# check_box_tag(:rock, disabled: true)
|
545
603
|
# #=> <input class="checkbox" disabled="disabled" ... type="checkbox" value="1">
|
546
|
-
#
|
547
|
-
def check_box_tag(name, attrs = {})
|
604
|
+
#
|
605
|
+
def check_box_tag(name, attrs = {})
|
606
|
+
name = name.to_s
|
607
|
+
attrs = {} if attrs.nil?
|
608
|
+
|
548
609
|
attrs.reverse_merge!(name: name, type: :checkbox, checked: false, value: 1)
|
549
610
|
attrs = add_css_id(attrs, name)
|
550
611
|
attrs = add_css_class(attrs, :checkbox)
|
551
612
|
attrs = add_ui_hint(attrs)
|
552
613
|
tag(:input, attrs)
|
553
614
|
end
|
554
|
-
|
555
|
-
|
556
|
-
# Creates a radio button; use groups of radio buttons named the same to allow users to
|
615
|
+
alias checkbox_tag check_box_tag
|
616
|
+
|
617
|
+
# Creates a radio button; use groups of radio buttons named the same to allow users to
|
557
618
|
# select from a group of options.
|
558
|
-
#
|
559
|
-
#
|
560
|
-
#
|
619
|
+
#
|
620
|
+
# @param name [String] The field name used for :id & :name attributes
|
621
|
+
# @param attrs [Hash] HTML attributes for the input[@type=radio] tag
|
622
|
+
#
|
623
|
+
# @return [String] The generated HTML input[@type=radio] tag
|
624
|
+
#
|
625
|
+
# @example Basic usage
|
561
626
|
# radio_button_tag(:accept)
|
562
627
|
# #=> <input class="radio" id="accept_1" name="accept" type="radio" value="1">
|
563
|
-
#
|
564
|
-
# Providing a value:
|
565
|
-
#
|
628
|
+
#
|
629
|
+
# @example Providing a value:
|
566
630
|
# radio_button_tag(:rock, value: 'rock music')
|
567
631
|
# #=> <input ... type="radio" value="rock music">
|
568
|
-
#
|
569
|
-
# Setting a different
|
570
|
-
#
|
571
|
-
#
|
572
|
-
#
|
573
|
-
#
|
574
|
-
#
|
575
|
-
#
|
576
|
-
#
|
577
|
-
# radio_button_tag(:rock, class: 'big')
|
632
|
+
#
|
633
|
+
# @example Setting a different `:id`.
|
634
|
+
# radio_button_tag(:rock, id: 'some-id')
|
635
|
+
# #=> <input class="radio" id="some-id_1" name="rock" type="radio" value="1">
|
636
|
+
#
|
637
|
+
# @example Adding another CSS class
|
638
|
+
# NOTE! appends the the class to the default class +.radio+.
|
639
|
+
#
|
640
|
+
# radio_button_tag(:rock, class: 'big')
|
578
641
|
# #=> <input class="big radio" id="rock_1" name="rock" type="radio" value="1">
|
579
|
-
#
|
580
|
-
# Adds a
|
581
|
-
#
|
582
|
-
# radio_button_tag(:rock, ui_hint: 'a user hint')
|
642
|
+
#
|
643
|
+
# @example Adds a `:title` attribute when passed `:ui_hint`.
|
644
|
+
# radio_button_tag(:rock, ui_hint: 'a user hint')
|
583
645
|
# #=> <input ... title="a user hint" type="radio" value="1">
|
584
|
-
#
|
585
|
-
# Supports the
|
586
|
-
#
|
646
|
+
#
|
647
|
+
# @example Supports the `:disabled` & `:checked` attributes.
|
587
648
|
# radio_button_tag(:yes, checked: true)
|
588
649
|
# #=> <input checked="checked" class="checkbox" id="yes_1"...value="1">
|
589
|
-
#
|
650
|
+
#
|
590
651
|
# radio_button_tag(:yes, disabled: true)
|
591
652
|
# #=> <input disabled="disabled" class="checkbox" id="yes_1" ... value="1">
|
592
|
-
#
|
593
|
-
|
594
|
-
|
653
|
+
#
|
654
|
+
def radio_button_tag(name, attrs = {})
|
655
|
+
name = name.to_s
|
656
|
+
attrs = {} if attrs.nil?
|
657
|
+
|
595
658
|
attrs.reverse_merge!(name: name, type: :radio, checked: false, value: 1)
|
596
659
|
attrs = add_css_id(attrs, name)
|
597
660
|
# id_value = [field.to_s,'_',value].join
|
@@ -600,278 +663,411 @@ class Roda
|
|
600
663
|
attrs = add_ui_hint(attrs)
|
601
664
|
tag(:input, attrs)
|
602
665
|
end
|
603
|
-
|
604
|
-
|
666
|
+
alias radiobutton_tag radio_button_tag
|
667
|
+
|
605
668
|
# Creates a submit button with the text value as the caption.
|
606
|
-
#
|
607
|
-
#
|
608
|
-
#
|
669
|
+
#
|
670
|
+
# @param name [String] The field name used for :id & :name attributes
|
671
|
+
# @param attrs [Hash] HTML attributes for the input[@type=submit] tag
|
672
|
+
#
|
673
|
+
# @return [String] The generated HTML input[@type=submit] tag
|
674
|
+
#
|
675
|
+
# @example Basic usage
|
609
676
|
# <%= submit_tag %> || <%= submit_button %>
|
610
677
|
# => <input name="submit" type="submit" value="Save Form">
|
611
678
|
#
|
612
679
|
# <%= submit_tag(nil) %>
|
613
680
|
# => <input name="submit" type="submit" value="">
|
614
|
-
#
|
681
|
+
#
|
615
682
|
# <%= submit_tag("Custom Value") %>
|
616
683
|
# => <input name="submit" type="submit" value="Custom Value">
|
617
|
-
#
|
618
|
-
# Adding a CSS class.
|
619
|
-
#
|
684
|
+
#
|
685
|
+
# @example Adding a CSS class.
|
686
|
+
# NOTE! input[:submit] has no other class by default.
|
687
|
+
#
|
620
688
|
# <%= submit_tag(class: 'some-class') %>
|
621
689
|
# #=> <input class="some-class" name="submit" type="submit" value="Save Form">
|
622
|
-
#
|
623
|
-
# Supports the
|
624
|
-
#
|
690
|
+
#
|
691
|
+
# @example Supports the `:disabled` attribute.
|
625
692
|
# <%= submit_tag(disabled: true) %>
|
626
693
|
# #=> <input disabled="disabled" name="submit" type="submit" value="Save Form">
|
627
|
-
#
|
628
|
-
# Adds a
|
629
|
-
#
|
694
|
+
#
|
695
|
+
# @example Adds a `:title` attribute when passed `:ui_hint`. Also works with `:title`.
|
630
696
|
# <%= submit_tag(ui_hint: 'a user hint') %>
|
631
697
|
# #=> <input name="submit" title="a user hint" type="submit" value="Save Form">
|
632
|
-
#
|
633
|
-
def submit_tag(value = 'Save Form', attrs = {})
|
634
|
-
|
698
|
+
#
|
699
|
+
def submit_tag(value = 'Save Form', attrs = {})
|
700
|
+
if value.is_a?(Hash)
|
701
|
+
attrs = value
|
702
|
+
value = 'Save Form'
|
703
|
+
end
|
635
704
|
attrs.reverse_merge!(type: :submit, name: :submit, value: value)
|
636
705
|
attrs = add_ui_hint(attrs)
|
637
706
|
self_closing_tag(:input, attrs)
|
638
707
|
end
|
639
|
-
|
640
|
-
|
641
|
-
#
|
642
|
-
#
|
643
|
-
#
|
644
|
-
#
|
645
|
-
#
|
646
|
-
#
|
647
|
-
#
|
648
|
-
#
|
649
|
-
#
|
650
|
-
#
|
651
|
-
#
|
652
|
-
#
|
653
|
-
# image_submit_tag(
|
654
|
-
# #=> <input class="
|
655
|
-
#
|
708
|
+
alias submit_button submit_tag
|
709
|
+
|
710
|
+
# Creates a image submit button which submits the form when clicked.
|
711
|
+
#
|
712
|
+
# @param src [String] The URL/path to the image
|
713
|
+
# @param attrs [Hash] HTML attributes for the input[@type=image] tag
|
714
|
+
#
|
715
|
+
# @return [String] The generated HTML input[@type=image] tag
|
716
|
+
#
|
717
|
+
# @example Basic usage
|
718
|
+
# image_submit_tag('/images/submit.png')
|
719
|
+
# #=> <input src="/images/submit.png" type="image">
|
720
|
+
#
|
721
|
+
# @example With attributes
|
722
|
+
# image_submit_tag('/images/submit.png', class: 'submit-button')
|
723
|
+
# #=> <input class="submit-button" src="/images/submit.png" type="image">
|
724
|
+
#
|
725
|
+
# @example With disabled state
|
726
|
+
# image_submit_tag('/images/submit.png', disabled: true)
|
727
|
+
# #=> <input disabled="disabled" src="/images/submit.png" type="image">
|
728
|
+
#
|
656
729
|
def image_submit_tag(src, attrs = {})
|
657
730
|
tag(:input, { type: :image, src: src }.merge(attrs))
|
658
731
|
end
|
659
|
-
|
660
|
-
|
732
|
+
alias imagesubmit_tag image_submit_tag
|
733
|
+
|
661
734
|
# Creates a reset button with the text value as the caption.
|
662
|
-
#
|
663
|
-
#
|
664
|
-
#
|
735
|
+
#
|
736
|
+
# @param value [String] The button text
|
737
|
+
# @param attrs [Hash] HTML attributes for the input[@type=reset] tag
|
738
|
+
#
|
739
|
+
# @return [String] The generated HTML input[@type=reset] tag
|
740
|
+
#
|
741
|
+
# @example Basic usage
|
665
742
|
# <%= reset_tag %>
|
666
743
|
# => <input name="reset" type="reset" value="Reset Form">
|
667
744
|
#
|
668
745
|
# <%= reset_tag(nil) %>
|
669
746
|
# => <input name="reset" type="reset" value="">
|
670
|
-
#
|
671
|
-
# Adding a CSS class
|
672
|
-
#
|
747
|
+
#
|
748
|
+
# @example Adding a CSS class
|
749
|
+
# NOTE! input[:reset] has no other class by default.
|
750
|
+
#
|
673
751
|
# <%= reset_tag('Custom Value', class: 'some-class') %>
|
674
752
|
# => <input class="some-class" name="reset" type="reset" value="Custom Value" >
|
675
|
-
#
|
676
|
-
# Supports the
|
677
|
-
#
|
753
|
+
#
|
754
|
+
# @example Supports the `:disabled` attribute.
|
678
755
|
# <%= reset_tag('Custom Value', disabled: true) %>
|
679
|
-
# => <input disabled="disabled" name="reset" type="reset" value="Custom Value">
|
680
|
-
#
|
681
|
-
# Adds a
|
682
|
-
#
|
756
|
+
# => <input disabled="disabled" name="reset" type="reset" value="Custom Value">
|
757
|
+
#
|
758
|
+
# @example Adds a `:title` attribute when passed `:ui_hint`.
|
683
759
|
# <%= reset_tag('Custom Value', ui_hint: 'a user hint') %>
|
684
|
-
# => <input name="reset" title="a user hint" type="
|
685
|
-
#
|
760
|
+
# => <input name="reset" title="a user hint" type="reset" value="Custom Value">
|
761
|
+
#
|
686
762
|
def reset_tag(value = 'Reset Form', attrs = {})
|
687
|
-
|
763
|
+
if value.is_a?(Hash)
|
764
|
+
attrs = value
|
765
|
+
value = 'Reset Form'
|
766
|
+
end
|
688
767
|
attrs.reverse_merge!(type: :reset, name: :reset, value: value)
|
689
768
|
attrs = add_ui_hint(attrs)
|
690
769
|
self_closing_tag(:input, attrs)
|
691
770
|
end
|
692
|
-
|
693
|
-
|
694
|
-
# Creates a dropdown
|
695
|
-
#
|
696
|
-
#
|
697
|
-
#
|
698
|
-
#
|
699
|
-
#
|
700
|
-
#
|
701
|
-
#
|
702
|
-
#
|
703
|
-
#
|
704
|
-
#
|
705
|
-
#
|
706
|
-
#
|
707
|
-
#
|
708
|
-
#
|
709
|
-
#
|
710
|
-
#
|
711
|
-
#
|
712
|
-
#
|
713
|
-
#
|
714
|
-
#
|
715
|
-
#
|
716
|
-
#
|
717
|
-
#
|
718
|
-
#
|
719
|
-
#
|
720
|
-
#
|
721
|
-
#
|
722
|
-
#
|
723
|
-
#
|
724
|
-
#
|
725
|
-
#
|
726
|
-
#
|
727
|
-
#
|
728
|
-
# <option value="b">B</option>
|
729
|
-
# </select>
|
730
|
-
#
|
731
|
-
#
|
732
|
-
# With Options values as an Array
|
733
|
-
#
|
771
|
+
alias reset_button reset_tag
|
772
|
+
|
773
|
+
# Creates a select (dropdown menu) form element with options
|
774
|
+
# Automatically handles single vs multiple selection modes
|
775
|
+
#
|
776
|
+
# @param name [String, Symbol] The name/id for the select element
|
777
|
+
# @param options [Array, Hash] The options to populate the dropdown
|
778
|
+
# Can be array of [value, text] pairs or hash of value => text mappings
|
779
|
+
# @param attrs [Hash] HTML attributes to apply to the select element
|
780
|
+
# Special options include:
|
781
|
+
# - :selected => Value(s) to mark as selected
|
782
|
+
# - :multiple => Allow multiple selections
|
783
|
+
# - :prompt => Add placeholder prompt option
|
784
|
+
#
|
785
|
+
# NOTE! the format for the options values must be [value, key].
|
786
|
+
#
|
787
|
+
# @return [String] The generated HTML select element with options
|
788
|
+
#
|
789
|
+
# @example Basic dropdown with array options
|
790
|
+
# select_tag(:color, [['red', 'Red'], ['blue', 'Blue']])
|
791
|
+
# #=> <select id="color" name="color">
|
792
|
+
# # <option value="red">Red</option>
|
793
|
+
# # <option value="blue">Blue</option>
|
794
|
+
# # </select>
|
795
|
+
#
|
796
|
+
# @example Multiple select with hash options
|
797
|
+
#
|
798
|
+
# NOTE! the [] on the `:name` attribute
|
799
|
+
#
|
800
|
+
# select_tag(:colors, {red: 'Red', blue: 'Blue'}, multiple: true)
|
801
|
+
# #=> <select id="colors" name="colors[]" multiple="multiple">
|
802
|
+
# # <option value="red">Red</option>
|
803
|
+
# # <option value="blue">Blue</option>
|
804
|
+
# # <snip...>
|
805
|
+
#
|
806
|
+
# @example With Selected Option value
|
734
807
|
# select_tag(:letters, @letters, selected: :a)
|
735
|
-
# #=>
|
736
|
-
#
|
737
|
-
#
|
738
|
-
#
|
739
|
-
#
|
740
|
-
#
|
741
|
-
#
|
742
|
-
#
|
808
|
+
# #=> <select id="letters" name="letters">
|
809
|
+
# # <option selected="selected" value="a">A</option>
|
810
|
+
# # <snip...>
|
811
|
+
#
|
812
|
+
# @example With Multiple Selected Options Array
|
813
|
+
#
|
814
|
+
# NOTE! the [] on the `:name` attribute and the select menu automatically
|
815
|
+
# becomes a multiple select box.
|
816
|
+
#
|
743
817
|
# select_tag(:letters, @letters, selected: [:a,'b'])
|
744
|
-
# #=>
|
745
|
-
#
|
746
|
-
#
|
747
|
-
#
|
748
|
-
#
|
749
|
-
#
|
750
|
-
# When { multiple: true }, the select menu becomes a select box allowing multiple choices.
|
751
|
-
# <b>NB! the [] on the <tt>:name</tt> attribute</b>
|
752
|
-
#
|
753
|
-
# select_tag(:letters, @letters, multiple: true)
|
754
|
-
# #=>
|
755
|
-
# <select id="letters" name="letters[]" multiple="multiple">
|
756
|
-
# <snip...>
|
757
|
-
#
|
758
|
-
# select_tag(:letters, @letters, disabled: true)
|
759
|
-
# #=>
|
760
|
-
# <select id="letters" disabled="disabled" name="letters">
|
761
|
-
# <snip...>
|
762
|
-
#
|
818
|
+
# #=> <select id="letters" multiple="multiple" name="letters[]">
|
819
|
+
# # <option selected="selected" value="a">A</option>
|
820
|
+
# # <option selected="selected" value="b">B</option>
|
821
|
+
# # <snip...>
|
822
|
+
#
|
823
|
+
# @example With custom `:id` attribute
|
763
824
|
# select_tag(:letters, @letters, id: 'my-letters')
|
764
|
-
# #=>
|
765
|
-
#
|
766
|
-
#
|
767
|
-
#
|
825
|
+
# #=> <select id="my-letters" name="letters">
|
826
|
+
# # <snip...>
|
827
|
+
#
|
828
|
+
# @example With custom `:class` attribute
|
768
829
|
# select_tag(:letters, @letters, class: 'funky-select')
|
769
|
-
# #=>
|
770
|
-
#
|
771
|
-
#
|
772
|
-
#
|
830
|
+
# #=> <select class="funky-select" id="my-letters" name="letters">
|
831
|
+
# # <snip...>
|
832
|
+
#
|
833
|
+
# @example With `prompt: true` attribute
|
773
834
|
# select_tag(:letters, @letters, prompt: true)
|
774
|
-
# #=>
|
775
|
-
#
|
776
|
-
#
|
777
|
-
#
|
778
|
-
#
|
779
|
-
# select_tag(:letters, @letters, prompt: 'Top Letters'
|
780
|
-
# #=>
|
781
|
-
#
|
782
|
-
#
|
783
|
-
|
784
|
-
#
|
785
|
-
#
|
786
|
-
#
|
787
|
-
|
835
|
+
# #=> <select id="letters" name="letters">
|
836
|
+
# # <option selected="selected" value="">- Select -</option>
|
837
|
+
# # <snip...>
|
838
|
+
#
|
839
|
+
# @example With `prompt: 'Custom'` attribute
|
840
|
+
# select_tag(:letters, @letters, prompt: 'Top Letters')
|
841
|
+
# #=> <select id="letters" name="letters">
|
842
|
+
# # <option value="">Top Letters</option>
|
843
|
+
# # <snip...>
|
844
|
+
|
845
|
+
# @example With `disabled: true` attribute
|
846
|
+
# select_tag(:letters, @letters, disabled: true)
|
847
|
+
# #=> <select id="letters" disabled="disabled" name="letters">
|
848
|
+
# # <snip...>
|
849
|
+
#
|
850
|
+
# rubocop:disable Metrics/AbcSize
|
851
|
+
def select_tag(name, options, attrs = {})
|
852
|
+
name = name.to_s
|
853
|
+
attrs = {} if attrs.nil?
|
854
|
+
|
788
855
|
options = options.to_a.reverse if options.is_a?(Hash)
|
789
856
|
attrs[:multiple] = true if attrs[:selected].is_a?(Array)
|
790
857
|
options_html = select_options(options, attrs)
|
791
858
|
attrs.delete(:selected)
|
792
859
|
# attrs = add_css_id(attrs, name)
|
793
860
|
add_css_id(attrs, name)
|
794
|
-
|
861
|
+
|
862
|
+
html_name = attrs[:multiple] == true && !name.to_s.end_with?('[]') ? "#{name}[]" : name
|
863
|
+
|
795
864
|
tag(:select, options_html, { name: html_name }.merge(attrs))
|
796
865
|
end
|
797
|
-
|
798
|
-
|
799
|
-
#
|
800
|
-
#
|
801
|
-
#
|
802
|
-
#
|
803
|
-
#
|
804
|
-
#
|
805
|
-
#
|
866
|
+
# rubocop:enable Metrics/AbcSize
|
867
|
+
|
868
|
+
# Creates an option tag for use in a select dropdown menu.
|
869
|
+
#
|
870
|
+
# @param value [String] The value attribute for the option
|
871
|
+
# @param key [String] The text content shown to the user
|
872
|
+
# @param attrs [Hash] Additional HTML attributes for the option tag
|
873
|
+
#
|
874
|
+
# @return [String] The generated HTML option tag
|
875
|
+
#
|
876
|
+
# @example Basic usage
|
877
|
+
# select_option('a', 'Letter A')
|
878
|
+
# #=> <option value="a">Letter A</option>
|
879
|
+
#
|
880
|
+
# @example When key is blank, titleizes value
|
881
|
+
# select_option('on', '')
|
882
|
+
# #=> <option value="on">On</option>
|
883
|
+
#
|
884
|
+
# @example With selected attribute
|
806
885
|
# select_option('a', 'Letter A', selected: true)
|
807
886
|
# #=> <option selected="selected" value="a">Letter A</option>
|
808
|
-
#
|
809
|
-
# select_option('a', 'Letter A', selected: false)
|
810
|
-
# #=> <option value="a">Letter A</option>
|
811
|
-
#
|
887
|
+
#
|
812
888
|
def select_option(value, key, attrs = {})
|
813
889
|
key = value.to_s.titleize if key.blank?
|
814
890
|
tag(:option, key, { value: value }.merge(attrs))
|
815
891
|
end
|
816
|
-
|
817
|
-
#
|
818
|
-
#
|
892
|
+
|
893
|
+
# Creates a hidden input field for HTTP method override support (e.g. PUT/DELETE requests)
|
894
|
+
# Used internally by form_tag for non-GET/POST methods.
|
895
|
+
# Supports `Rack::MethodOverride`
|
896
|
+
#
|
897
|
+
# @param method [String] The HTTP method to override with (default: 'PUT')
|
898
|
+
# @return [String] Hidden input field with _method override
|
899
|
+
#
|
900
|
+
# @example Basic usage
|
901
|
+
# faux_method('DELETE')
|
902
|
+
# #=> <input name="_method" type="hidden" value="DELETE">
|
903
|
+
#
|
904
|
+
# @example Default PUT method
|
905
|
+
# faux_method
|
906
|
+
# #=> <input name="_method" type="hidden" value="PUT">
|
907
|
+
#
|
819
908
|
def faux_method(method = 'PUT')
|
820
909
|
hidden_field_tag(:input, name: '_method', value: method.to_s.upcase)
|
821
910
|
end
|
822
|
-
|
823
|
-
|
911
|
+
|
824
912
|
private
|
825
|
-
|
826
|
-
#
|
913
|
+
|
914
|
+
# Returns the tag helper options hash from Roda options.
|
915
|
+
# These options control default HTML attributes and formatting for generated tags.
|
916
|
+
#
|
917
|
+
# @return [Hash] The tag helper configuration options
|
918
|
+
#
|
919
|
+
# @example Basic usage
|
920
|
+
# opts_tag_helpers[:tags_label_required_str]
|
921
|
+
# #=> '<span>*</span>'
|
922
|
+
#
|
827
923
|
def opts_tag_helpers
|
828
924
|
opts[:tag_helpers]
|
829
925
|
end
|
830
|
-
|
926
|
+
|
927
|
+
# Converts a string to an HTML safe ID attribute value
|
928
|
+
# - Converts to lowercase
|
929
|
+
# - Replaces non-word characters with hyphens
|
930
|
+
# - Collapses multiple hyphens into single hyphens
|
931
|
+
#
|
932
|
+
# @param id [String, #to_s] The value to convert to an HTML safe ID
|
933
|
+
#
|
934
|
+
# @return [String] The sanitized ID value
|
831
935
|
#
|
832
|
-
|
936
|
+
# @example Basic usage
|
937
|
+
# html_safe_id("Hello World!")
|
938
|
+
# #=> "hello-world-"
|
939
|
+
#
|
940
|
+
# @example With symbols
|
941
|
+
# html_safe_id(:hello_world)
|
942
|
+
# #=> "hello-world"
|
943
|
+
#
|
944
|
+
# @example Collapsing multiple hyphens
|
945
|
+
# html_safe_id("too--many---hyphens")
|
946
|
+
# #=> "too-many-hyphens"
|
947
|
+
#
|
948
|
+
def html_safe_id(id)
|
833
949
|
id.to_s.downcase.gsub(/\W/, '-').gsub('--', '-')
|
834
950
|
end
|
835
|
-
|
836
|
-
#
|
951
|
+
|
952
|
+
# Adds CSS classes to the HTML attributes hash.
|
953
|
+
# Merges new classes with any existing classes, preserving existing ones.
|
954
|
+
#
|
955
|
+
# @param attrs [Hash] HTML attributes hash
|
956
|
+
# @param new_class [String, Symbol, nil] Additional CSS class(es) to add
|
957
|
+
#
|
958
|
+
# @return [Hash] Updated attributes hash with merged classes
|
959
|
+
#
|
960
|
+
# @example Basic usage
|
961
|
+
# add_css_class({}, 'btn')
|
962
|
+
# #=> { class: 'btn' }
|
963
|
+
#
|
964
|
+
# @example With existing classes
|
965
|
+
# add_css_class({class: 'red'}, 'btn')
|
966
|
+
# #=> { class: 'red btn' }
|
967
|
+
#
|
968
|
+
# @example With nil new_class
|
969
|
+
# add_css_class({class: 'red'}, nil)
|
970
|
+
# #=> { class: 'red' }
|
971
|
+
#
|
837
972
|
def add_css_class(attrs, new_class = nil)
|
838
973
|
merge_attr_classes(attrs, new_class)
|
839
974
|
end
|
840
|
-
|
975
|
+
|
976
|
+
# Adds or updates the ID attribute in the HTML attributes hash.
|
977
|
+
# Handles nil/empty values and sanitizes the ID for HTML safety.
|
978
|
+
#
|
979
|
+
# @param attrs [Hash, nil] HTML attributes hash to modify
|
980
|
+
# @param new_id [String, Symbol, Hash] New ID value to set
|
981
|
+
#
|
982
|
+
# @return [Hash] Updated attributes hash with ID added/modified
|
983
|
+
#
|
984
|
+
# @example Basic usage
|
985
|
+
# add_css_id({}, 'my-id')
|
986
|
+
# #=> { id: 'my-id' }
|
987
|
+
#
|
988
|
+
# @example With existing ID
|
989
|
+
# add_css_id({id: 'old'}, 'new')
|
990
|
+
# #=> { id: 'old' }
|
841
991
|
#
|
842
|
-
|
992
|
+
# @example With false ID
|
993
|
+
# add_css_id({}, false)
|
994
|
+
# #=> {} # No ID added
|
995
|
+
#
|
996
|
+
# @example With empty ID
|
997
|
+
# add_css_id({}, '')
|
998
|
+
# #=> {} # ID removed
|
999
|
+
#
|
1000
|
+
def add_css_id(attrs, new_id)
|
843
1001
|
attrs = {} if attrs.nil?
|
844
1002
|
new_id = '' if new_id.is_a?(Hash)
|
845
|
-
id_value = attrs[:id].nil? ? html_safe_id(new_id.to_s) : attrs.delete(:id)
|
1003
|
+
id_value = attrs[:id].nil? ? html_safe_id(new_id.to_s) : attrs.delete(:id)
|
846
1004
|
attrs[:id] = id_value.to_s unless id_value == false
|
847
1005
|
attrs[:id] = nil if attrs[:id] == '' # set to nil to remove from tag output
|
848
1006
|
attrs
|
849
1007
|
end
|
850
|
-
|
1008
|
+
|
1009
|
+
# Adds a title attribute to HTML elements based on ui_hint option
|
1010
|
+
# Moves the :ui_hint value to :title if present
|
1011
|
+
#
|
1012
|
+
# @param attrs [Hash] HTML attributes hash
|
1013
|
+
#
|
1014
|
+
# @return [Hash] Updated attributes hash with ui_hint moved to title
|
851
1015
|
#
|
852
|
-
|
1016
|
+
# @example Basic usage
|
1017
|
+
# add_ui_hint({ui_hint: 'Help text'})
|
1018
|
+
# #=> {title: 'Help text'}
|
1019
|
+
#
|
1020
|
+
# @example With no ui_hint
|
1021
|
+
# add_ui_hint({class: 'btn'})
|
1022
|
+
# #=> {class: 'btn'}
|
1023
|
+
#
|
1024
|
+
def add_ui_hint(attrs)
|
853
1025
|
attrs[:title] = attrs.delete(:ui_hint) unless attrs[:ui_hint].nil?
|
854
1026
|
attrs
|
855
1027
|
end
|
856
|
-
|
857
|
-
#
|
858
|
-
#
|
859
|
-
#
|
860
|
-
#
|
861
|
-
#
|
862
|
-
#
|
863
|
-
#
|
864
|
-
#
|
865
|
-
#
|
1028
|
+
|
1029
|
+
# Creates and returns select option elements from an array of values or hash
|
1030
|
+
# Handles optgroups, selected values, and prompts
|
1031
|
+
#
|
1032
|
+
# @param values [Array, Hash] The values to create options from. Can be:
|
1033
|
+
# - Array of [value, text] pairs: `[["a", "Option A"], ["b", "Option B"]]`
|
1034
|
+
# - Hash of value => text pairs: `{"a" => "Option A", "b" => "Option B"}`
|
1035
|
+
# - Nested hash for optgroups: `{"Group 1" => {"a" => "Option A"}}`
|
1036
|
+
# @param attrs [Hash] HTML attributes hash containing selection options
|
1037
|
+
# @option attrs [String, Array] :selected Value(s) to mark as selected
|
1038
|
+
# @option attrs [String, true] :prompt Optional prompt text or true for default
|
1039
|
+
#
|
1040
|
+
# @return [String] Generated HTML option tags
|
1041
|
+
#
|
1042
|
+
# @example Basic array of options
|
1043
|
+
# select_options([["a", "Option A"], ["b", "Option B"]])
|
1044
|
+
# #=> <option value="a">Option A</option>
|
1045
|
+
# # <option value="b">Option B</option>
|
1046
|
+
#
|
1047
|
+
# @example With selected value
|
1048
|
+
# select_options([["a", "A"], ["b", "B"]], selected: "a")
|
1049
|
+
# #=> <option value="a" selected="selected">A</option>
|
1050
|
+
# # <option value="b">B</option>
|
1051
|
+
#
|
1052
|
+
# @example With optgroups
|
1053
|
+
# select_options({ 'Group 1' => {:a => 'A' }, 'Group 2' => { 'b' => 'B' } })
|
1054
|
+
# #=> <optgroup label="Group 1">
|
1055
|
+
# # <option value="a">A</option>
|
1056
|
+
# # </optgroup>
|
1057
|
+
# # <optgroup label="Group 2">
|
1058
|
+
# # <option value="b">B</option>
|
1059
|
+
# # </optgroup>
|
1060
|
+
#
|
1061
|
+
# rubocop:disable Metrics/MethodLength
|
866
1062
|
def select_options(values, attrs = {})
|
867
|
-
attrs = {} if attrs.blank?
|
1063
|
+
attrs = {} if attrs.blank?
|
868
1064
|
values = [] if values.blank?
|
869
1065
|
normalize_select_prompt(values, attrs)
|
870
1066
|
# { :a => 'A' }
|
871
1067
|
# [5, 'E']
|
872
|
-
# FIXME:: when passed a Hash of values, they become reversed (last first and so on..)
|
873
|
-
|
874
|
-
values.map do |value, key|
|
1068
|
+
# FIXME:: when passed a Hash of values, they become reversed (last first and so on..)
|
1069
|
+
|
1070
|
+
values.map do |value, key|
|
875
1071
|
if value.is_a?(Hash)
|
876
1072
|
tag(:optgroup, select_options(value, attrs), label: key)
|
877
1073
|
elsif option_selected?(value, attrs[:selected])
|
@@ -881,39 +1077,63 @@ class Roda
|
|
881
1077
|
end
|
882
1078
|
end.join
|
883
1079
|
end
|
884
|
-
|
885
|
-
|
886
|
-
#
|
887
|
-
#
|
888
|
-
#
|
889
|
-
#
|
890
|
-
#
|
891
|
-
|
1080
|
+
# rubocop:enable Metrics/MethodLength
|
1081
|
+
|
1082
|
+
# Normalizes the prompt option for select dropdowns
|
1083
|
+
# - Removes prompt from attributes after processing
|
1084
|
+
# - Sets blank option as selected by default unless explicit selection
|
1085
|
+
# - Uses default "- Select -" text if prompt is true
|
1086
|
+
# - Adds prompt as first empty option in values array
|
1087
|
+
#
|
1088
|
+
# @param values [Array] Array of select options to prepend prompt to
|
1089
|
+
# @param attrs [Hash] Attributes hash containing prompt option
|
1090
|
+
#
|
1091
|
+
# @example With true prompt
|
1092
|
+
# normalize_select_prompt(values, prompt: true)
|
1093
|
+
# # Adds ["", "- Select -"] as first option
|
1094
|
+
#
|
1095
|
+
# @example With custom prompt text
|
1096
|
+
# normalize_select_prompt(values, prompt: "Choose one")
|
1097
|
+
# # Adds ["", "Choose one"] as first option
|
1098
|
+
#
|
1099
|
+
def normalize_select_prompt(values, attrs = {})
|
892
1100
|
return unless attrs.key?(:prompt)
|
1101
|
+
|
893
1102
|
prompt = attrs.delete(:prompt)
|
894
1103
|
attrs[:selected] = '' unless attrs.include?(:selected)
|
895
1104
|
prompt_text = prompt == true ? '- Select -' : prompt
|
896
1105
|
values.unshift(['', prompt_text])
|
897
1106
|
end
|
898
|
-
|
899
|
-
#
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
1107
|
+
|
1108
|
+
# Checks if a select option value should be marked as selected
|
1109
|
+
#
|
1110
|
+
# @param key [String, Symbol] The option value to check
|
1111
|
+
# @param selection [String, Symbol, Array] The currently selected value(s)
|
1112
|
+
#
|
1113
|
+
# @return [Boolean] true if option should be selected, false otherwise
|
1114
|
+
#
|
1115
|
+
# @example With single selection
|
1116
|
+
# option_selected?('a', 'a') #=> true
|
1117
|
+
# option_selected?('a', 'b') #=> false
|
1118
|
+
#
|
1119
|
+
# @example With array of selections
|
1120
|
+
# option_selected?('a', ['a', 'b']) #=> true
|
1121
|
+
# option_selected?('c', ['a', 'b']) #=> false
|
1122
|
+
#
|
1123
|
+
def option_selected?(key, selection)
|
1124
|
+
if selection.is_a?(Array)
|
1125
|
+
selection.map(&:to_s).include?(key.to_s)
|
905
1126
|
else
|
906
1127
|
selection.to_s == key.to_s
|
907
1128
|
end
|
908
1129
|
end
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
1130
|
+
end
|
1131
|
+
# rubocop:enable Metrics/ModuleLength
|
1132
|
+
# /InstanceMethods
|
1133
|
+
end
|
1134
|
+
# /RodaTagHelpers
|
1135
|
+
|
915
1136
|
register_plugin(:tag_helpers, RodaTagHelpers)
|
916
|
-
|
917
|
-
|
918
|
-
|
1137
|
+
end
|
1138
|
+
# /RodaPlugins
|
919
1139
|
end
|