lotus-helpers 0.1.0 → 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 +4 -4
- data/README.md +167 -1
- data/lib/lotus/helpers.rb +6 -0
- data/lib/lotus/helpers/form_helper.rb +423 -0
- data/lib/lotus/helpers/form_helper/form_builder.rb +790 -0
- data/lib/lotus/helpers/form_helper/html_node.rb +79 -0
- data/lib/lotus/helpers/form_helper/values.rb +38 -0
- data/lib/lotus/helpers/html_helper.rb +1 -1
- data/lib/lotus/helpers/html_helper/html_builder.rb +11 -2
- data/lib/lotus/helpers/html_helper/html_node.rb +2 -1
- data/lib/lotus/helpers/link_to_helper.rb +28 -0
- data/lib/lotus/helpers/number_formatting_helper.rb +220 -0
- data/lib/lotus/helpers/version.rb +1 -1
- data/lotus-helpers.gemspec +1 -1
- metadata +11 -5
@@ -0,0 +1,790 @@
|
|
1
|
+
require 'lotus/helpers/form_helper/html_node'
|
2
|
+
require 'lotus/helpers/form_helper/values'
|
3
|
+
require 'lotus/helpers/html_helper/html_builder'
|
4
|
+
require 'lotus/utils/string'
|
5
|
+
|
6
|
+
module Lotus
|
7
|
+
module Helpers
|
8
|
+
module FormHelper
|
9
|
+
# Form builder
|
10
|
+
#
|
11
|
+
# @since x.x.x
|
12
|
+
#
|
13
|
+
# @see Lotus::Helpers::HtmlHelper::HtmlBuilder
|
14
|
+
class FormBuilder < ::Lotus::Helpers::HtmlHelper::HtmlBuilder
|
15
|
+
# Set of HTTP methods that are understood by web browsers
|
16
|
+
#
|
17
|
+
# @since x.x.x
|
18
|
+
# @api private
|
19
|
+
BROWSER_METHODS = ['GET', 'POST'].freeze
|
20
|
+
|
21
|
+
# Checked attribute value
|
22
|
+
#
|
23
|
+
# @since x.x.x
|
24
|
+
# @api private
|
25
|
+
#
|
26
|
+
# @see Lotus::Helpers::FormHelper::FormBuilder#radio_button
|
27
|
+
CHECKED = 'checked'.freeze
|
28
|
+
|
29
|
+
# Selected attribute value for option
|
30
|
+
#
|
31
|
+
# @since x.x.x
|
32
|
+
# @api private
|
33
|
+
#
|
34
|
+
# @see Lotus::Helpers::FormHelper::FormBuilder#select
|
35
|
+
SELECTED = 'selected'.freeze
|
36
|
+
|
37
|
+
# Separator for accept attribute of file input
|
38
|
+
#
|
39
|
+
# @since x.x.x
|
40
|
+
# @api private
|
41
|
+
#
|
42
|
+
# @see Lotus::Helpers::FormHelper::FormBuilder#file_input
|
43
|
+
ACCEPT_SEPARATOR = ','.freeze
|
44
|
+
|
45
|
+
# Replacement for input id interpolation
|
46
|
+
#
|
47
|
+
# @since x.x.x
|
48
|
+
# @api private
|
49
|
+
#
|
50
|
+
# @see Lotus::Helpers::FormHelper::FormBuilder#_input_id
|
51
|
+
INPUT_ID_REPLACEMENT = '-\k<token>'.freeze
|
52
|
+
|
53
|
+
# Replacement for input value interpolation
|
54
|
+
#
|
55
|
+
# @since x.x.x
|
56
|
+
# @api private
|
57
|
+
#
|
58
|
+
# @see Lotus::Helpers::FormHelper::FormBuilder#_value
|
59
|
+
INPUT_VALUE_REPLACEMENT = '.\k<token>'.freeze
|
60
|
+
|
61
|
+
# Default value for unchecked check box
|
62
|
+
#
|
63
|
+
# @since x.x.x
|
64
|
+
# @api private
|
65
|
+
#
|
66
|
+
# @see Lotus::Helpers::FormHelper::FormBuilder#check_box
|
67
|
+
DEFAULT_UNCHECKED_VALUE = '0'.freeze
|
68
|
+
|
69
|
+
# Default value for checked check box
|
70
|
+
#
|
71
|
+
# @since x.x.x
|
72
|
+
# @api private
|
73
|
+
#
|
74
|
+
# @see Lotus::Helpers::FormHelper::FormBuilder#check_box
|
75
|
+
DEFAULT_CHECKED_VALUE = '1'.freeze
|
76
|
+
|
77
|
+
# ENCTYPE_MULTIPART = 'multipart/form-data'.freeze
|
78
|
+
|
79
|
+
self.html_node = ::Lotus::Helpers::FormHelper::HtmlNode
|
80
|
+
|
81
|
+
# Instantiate a form builder
|
82
|
+
#
|
83
|
+
# @overload initialize(form, attributes, params, &blk)
|
84
|
+
# Top level form
|
85
|
+
# @param form [Lotus::Helpers:FormHelper::Form] the form
|
86
|
+
# @param attributes [::Hash] a set of HTML attributes
|
87
|
+
# @param params [Lotus::Action::Params] request params
|
88
|
+
# @param blk [Proc] a block that describes the contents of the form
|
89
|
+
#
|
90
|
+
# @overload initialize(form, attributes, params, &blk)
|
91
|
+
# Nested form
|
92
|
+
# @param form [Lotus::Helpers:FormHelper::Form] the form
|
93
|
+
# @param attributes [Lotus::Helpers::FormHelper::Values] user defined
|
94
|
+
# values
|
95
|
+
# @param blk [Proc] a block that describes the contents of the form
|
96
|
+
#
|
97
|
+
# @return [Lotus::Helpers::FormHelper::FormBuilder] the form builder
|
98
|
+
#
|
99
|
+
# @since x.x.x
|
100
|
+
# @api private
|
101
|
+
def initialize(form, attributes, context = nil, &blk)
|
102
|
+
super()
|
103
|
+
|
104
|
+
@context = context
|
105
|
+
@blk = blk
|
106
|
+
|
107
|
+
# Nested form
|
108
|
+
if @context.nil? && attributes.is_a?(Values)
|
109
|
+
@values = attributes
|
110
|
+
@attributes = {}
|
111
|
+
@name = form
|
112
|
+
else
|
113
|
+
@form = form
|
114
|
+
@name = form.name
|
115
|
+
@values = Values.new(form.values, @context.params)
|
116
|
+
@attributes = attributes
|
117
|
+
@csrf_token = csrf_token
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Resolves all the nodes and generates the markup
|
122
|
+
#
|
123
|
+
# @return [Lotus::Utils::Escape::SafeString] the output
|
124
|
+
#
|
125
|
+
# @since x.x.x
|
126
|
+
# @api private
|
127
|
+
#
|
128
|
+
# @see Lotus::Helpers::HtmlHelper::HtmlBuilder#to_s
|
129
|
+
# @see http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/Escape/SafeString
|
130
|
+
def to_s
|
131
|
+
if toplevel?
|
132
|
+
_method_override!
|
133
|
+
form(@blk, @attributes)
|
134
|
+
end
|
135
|
+
|
136
|
+
super
|
137
|
+
end
|
138
|
+
|
139
|
+
# Nested fields
|
140
|
+
#
|
141
|
+
# The inputs generated by the wrapped block will be prefixed with the given name
|
142
|
+
# It supports infinite levels of nesting.
|
143
|
+
#
|
144
|
+
# @param name [Symbol] the nested name, it's used to generate input
|
145
|
+
# names, ids, and to lookup params to fill values.
|
146
|
+
#
|
147
|
+
# @since x.x.x
|
148
|
+
#
|
149
|
+
# @example Basic usage
|
150
|
+
# <%=
|
151
|
+
# form_for :delivery, routes.deliveries_path do
|
152
|
+
# text_field :customer_name
|
153
|
+
#
|
154
|
+
# fields_for :address do
|
155
|
+
# text_field :street
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
# submit 'Create'
|
159
|
+
# end
|
160
|
+
# %>
|
161
|
+
#
|
162
|
+
# Output:
|
163
|
+
# # <form action="/deliveries" method="POST" accept-charset="utf-8" id="delivery-form">
|
164
|
+
# # <input type="text" name="delivery[customer_name]" id="delivery-customer-name" value="">
|
165
|
+
# # <input type="text" name="delivery[address][street]" id="delivery-address-street" value="">
|
166
|
+
# #
|
167
|
+
# # <button type="submit">Create</button>
|
168
|
+
# # </form>
|
169
|
+
#
|
170
|
+
# @example Multiple levels of nesting
|
171
|
+
# <%=
|
172
|
+
# form_for :delivery, routes.deliveries_path do
|
173
|
+
# text_field :customer_name
|
174
|
+
#
|
175
|
+
# fields_for :address do
|
176
|
+
# text_field :street
|
177
|
+
#
|
178
|
+
# fields_for :location do
|
179
|
+
# text_field :city
|
180
|
+
# text_field :country
|
181
|
+
# end
|
182
|
+
# end
|
183
|
+
#
|
184
|
+
# submit 'Create'
|
185
|
+
# end
|
186
|
+
# %>
|
187
|
+
#
|
188
|
+
# Output:
|
189
|
+
# # <form action="/deliveries" method="POST" accept-charset="utf-8" id="delivery-form">
|
190
|
+
# # <input type="text" name="delivery[customer_name]" id="delivery-customer-name" value="">
|
191
|
+
# # <input type="text" name="delivery[address][street]" id="delivery-address-street" value="">
|
192
|
+
# # <input type="text" name="delivery[address][location][city]" id="delivery-address-location-city" value="">
|
193
|
+
# # <input type="text" name="delivery[address][location][country]" id="delivery-address-location-country" value="">
|
194
|
+
# #
|
195
|
+
# # <button type="submit">Create</button>
|
196
|
+
# # </form>
|
197
|
+
def fields_for(name)
|
198
|
+
current_name = @name
|
199
|
+
@name = _input_name(name)
|
200
|
+
yield
|
201
|
+
ensure
|
202
|
+
@name = current_name
|
203
|
+
end
|
204
|
+
|
205
|
+
# Label tag
|
206
|
+
#
|
207
|
+
# The first param <tt>content</tt> can be a <tt>Symbol</tt> that represents
|
208
|
+
# the target field (Eg. <tt>:extended_title</tt>), or a <tt>String</tt>
|
209
|
+
# which is used as it is.
|
210
|
+
#
|
211
|
+
# @param content [Symbol,String] the field name or a content string
|
212
|
+
# @param attributes [Hash] HTML attributes to pass to the label tag
|
213
|
+
#
|
214
|
+
# @since x.x.x
|
215
|
+
#
|
216
|
+
# @example Basic usage
|
217
|
+
# <%=
|
218
|
+
# # ...
|
219
|
+
# label :extended_title
|
220
|
+
# %>
|
221
|
+
#
|
222
|
+
# # Output:
|
223
|
+
# # <label for="book-extended-title">Extended Title</label>
|
224
|
+
#
|
225
|
+
# @example Custom content
|
226
|
+
# <%=
|
227
|
+
# # ...
|
228
|
+
# label 'Title', for: :extended_title
|
229
|
+
# %>
|
230
|
+
#
|
231
|
+
# # Output:
|
232
|
+
# # <label for="book-extended-title">Title</label>
|
233
|
+
#
|
234
|
+
# @example Nested fields usage
|
235
|
+
# <%=
|
236
|
+
# # ...
|
237
|
+
# fields_for :address do
|
238
|
+
# label :city
|
239
|
+
# text_field :city
|
240
|
+
# end
|
241
|
+
# %>
|
242
|
+
#
|
243
|
+
# # Output:
|
244
|
+
# # <label for="delivery-address-city">City</label>
|
245
|
+
# # <input type="text" name="delivery[address][city] id="delivery-address-city" value="">
|
246
|
+
def label(content, attributes = {})
|
247
|
+
attributes = { for: _for(content, attributes.delete(:for)) }.merge(attributes)
|
248
|
+
content = Utils::String.new(content).titleize
|
249
|
+
|
250
|
+
super(content, attributes)
|
251
|
+
end
|
252
|
+
|
253
|
+
# Check box
|
254
|
+
#
|
255
|
+
# It renders a check box input.
|
256
|
+
#
|
257
|
+
# When a form is submitted, browsers don't send the value of unchecked
|
258
|
+
# check boxes. If an user unchecks a check box, their browser won't send
|
259
|
+
# the unchecked value. On the server side the corresponding value is
|
260
|
+
# missing, so the application will assume that the user action never
|
261
|
+
# happened.
|
262
|
+
#
|
263
|
+
# To solve this problem the form renders a hidden field with the
|
264
|
+
# "unchecked value". When the user unchecks the input, the browser will
|
265
|
+
# ignore it, but it will still send the value of the hidden input. See
|
266
|
+
# the examples below.
|
267
|
+
#
|
268
|
+
# When editing a resource, the form automatically assigns the
|
269
|
+
# <tt>checked="checked"</tt> attribute.
|
270
|
+
#
|
271
|
+
# @param name [Symbol] the input name
|
272
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
273
|
+
# @option attributes [String] :checked_value (defaults to "1")
|
274
|
+
# @option attributes [String] :unchecked_value (defaults to "0")
|
275
|
+
#
|
276
|
+
# @since x.x.x
|
277
|
+
#
|
278
|
+
# @example Basic usage
|
279
|
+
# <%=
|
280
|
+
# check_box :free_shipping
|
281
|
+
# %>
|
282
|
+
#
|
283
|
+
# # Output:
|
284
|
+
# # <input type="hidden" name="delivery[free_shipping]" value="0">
|
285
|
+
# # <input type="checkbox" name="delivery[free_shipping]" id="delivery-free-shipping" value="1">
|
286
|
+
#
|
287
|
+
# @example Specify (un)checked values
|
288
|
+
# <%=
|
289
|
+
# check_box :free_shipping, checked_value: 'true', unchecked_value: 'false'
|
290
|
+
# %>
|
291
|
+
#
|
292
|
+
# # Output:
|
293
|
+
# # <input type="hidden" name="delivery[free_shipping]" value="false">
|
294
|
+
# # <input type="checkbox" name="delivery[free_shipping]" id="delivery-free-shipping" value="true">
|
295
|
+
#
|
296
|
+
# @example Automatic "checked" attribute
|
297
|
+
# # For this example the params are:
|
298
|
+
# #
|
299
|
+
# # { delivery: { free_shipping: '1' } }
|
300
|
+
# <%=
|
301
|
+
# check_box :free_shipping
|
302
|
+
# %>
|
303
|
+
#
|
304
|
+
# # Output:
|
305
|
+
# # <input type="hidden" name="delivery[free_shipping]" value="0">
|
306
|
+
# # <input type="checkbox" name="delivery[free_shipping]" id="delivery-free-shipping" value="1" checked="checked">
|
307
|
+
#
|
308
|
+
# @example Force "checked" attribute
|
309
|
+
# # For this example the params are:
|
310
|
+
# #
|
311
|
+
# # { delivery: { free_shipping: '0' } }
|
312
|
+
# <%=
|
313
|
+
# check_box :free_shipping, checked: 'checked'
|
314
|
+
# %>
|
315
|
+
#
|
316
|
+
# # Output:
|
317
|
+
# # <input type="hidden" name="delivery[free_shipping]" value="0">
|
318
|
+
# # <input type="checkbox" name="delivery[free_shipping]" id="delivery-free-shipping" value="1" checked="checked">
|
319
|
+
#
|
320
|
+
# @example Multiple check boxes
|
321
|
+
# <%=
|
322
|
+
# check_box :languages, name: 'book[languages][]', value: 'italian', id: nil
|
323
|
+
# check_box :languages, name: 'book[languages][]', value: 'english', id: nil
|
324
|
+
# %>
|
325
|
+
#
|
326
|
+
# # Output:
|
327
|
+
# # <input type="checkbox" name="book[languages][]" value="italian">
|
328
|
+
# # <input type="checkbox" name="book[languages][]" value="english">
|
329
|
+
#
|
330
|
+
# @example Automatic "checked" attribute for multiple check boxes
|
331
|
+
# # For this example the params are:
|
332
|
+
# #
|
333
|
+
# # { book: { languages: ['italian'] } }
|
334
|
+
# <%=
|
335
|
+
# check_box :languages, name: 'book[languages][]', value: 'italian', id: nil
|
336
|
+
# check_box :languages, name: 'book[languages][]', value: 'english', id: nil
|
337
|
+
# %>
|
338
|
+
#
|
339
|
+
# # Output:
|
340
|
+
# # <input type="checkbox" name="book[languages][]" value="italian" checked="checked">
|
341
|
+
# # <input type="checkbox" name="book[languages][]" value="english">
|
342
|
+
def check_box(name, attributes = {})
|
343
|
+
_hidden_field_for_check_box( name, attributes)
|
344
|
+
input _attributes_for_check_box(name, attributes)
|
345
|
+
end
|
346
|
+
|
347
|
+
# Color input
|
348
|
+
#
|
349
|
+
# @param name [Symbol] the input name
|
350
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
351
|
+
#
|
352
|
+
# @since x.x.x
|
353
|
+
#
|
354
|
+
# @example Basic usage
|
355
|
+
# <%=
|
356
|
+
# # ...
|
357
|
+
# color_field :background
|
358
|
+
# %>
|
359
|
+
#
|
360
|
+
# # Output:
|
361
|
+
# # <input type="color" name="user[background]" id="user-background" value="">
|
362
|
+
def color_field(name, attributes = {})
|
363
|
+
input _attributes(:color, name, attributes)
|
364
|
+
end
|
365
|
+
|
366
|
+
# Date input
|
367
|
+
#
|
368
|
+
# @param name [Symbol] the input name
|
369
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
370
|
+
#
|
371
|
+
# @since x.x.x
|
372
|
+
#
|
373
|
+
# @example Basic usage
|
374
|
+
# <%=
|
375
|
+
# # ...
|
376
|
+
# date_field :birth_date
|
377
|
+
# %>
|
378
|
+
#
|
379
|
+
# # Output:
|
380
|
+
# # <input type="date" name="user[birth_date]" id="user-birth-date" value="">
|
381
|
+
def date_field(name, attributes = {})
|
382
|
+
input _attributes(:date, name, attributes)
|
383
|
+
end
|
384
|
+
|
385
|
+
# Datetime input
|
386
|
+
#
|
387
|
+
# @param name [Symbol] the input name
|
388
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
389
|
+
#
|
390
|
+
# @since x.x.x
|
391
|
+
#
|
392
|
+
# @example Basic usage
|
393
|
+
# <%=
|
394
|
+
# # ...
|
395
|
+
# datetime_field :delivered_at
|
396
|
+
# %>
|
397
|
+
#
|
398
|
+
# # Output:
|
399
|
+
# # <input type="datetime" name="delivery[delivered_at]" id="delivery-delivered-at" value="">
|
400
|
+
def datetime_field(name, attributes = {})
|
401
|
+
input _attributes(:datetime, name, attributes)
|
402
|
+
end
|
403
|
+
|
404
|
+
# Datetime Local input
|
405
|
+
#
|
406
|
+
# @param name [Symbol] the input name
|
407
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
408
|
+
#
|
409
|
+
# @since x.x.x
|
410
|
+
#
|
411
|
+
# @example Basic usage
|
412
|
+
# <%=
|
413
|
+
# # ...
|
414
|
+
# datetime_local_field :delivered_at
|
415
|
+
# %>
|
416
|
+
#
|
417
|
+
# # Output:
|
418
|
+
# # <input type="datetime-local" name="delivery[delivered_at]" id="delivery-delivered-at" value="">
|
419
|
+
def datetime_local_field(name, attributes = {})
|
420
|
+
input _attributes(:'datetime-local', name, attributes)
|
421
|
+
end
|
422
|
+
|
423
|
+
# Email input
|
424
|
+
#
|
425
|
+
# @param name [Symbol] the input name
|
426
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
427
|
+
#
|
428
|
+
# @since x.x.x
|
429
|
+
#
|
430
|
+
# @example Basic usage
|
431
|
+
# <%=
|
432
|
+
# # ...
|
433
|
+
# email_field :email
|
434
|
+
# %>
|
435
|
+
#
|
436
|
+
# # Output:
|
437
|
+
# # <input type="email" name="user[email]" id="user-email" value="">
|
438
|
+
def email_field(name, attributes = {})
|
439
|
+
input _attributes(:email, name, attributes)
|
440
|
+
end
|
441
|
+
|
442
|
+
# Hidden input
|
443
|
+
#
|
444
|
+
# @param name [Symbol] the input name
|
445
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
446
|
+
#
|
447
|
+
# @since x.x.x
|
448
|
+
#
|
449
|
+
# @example Basic usage
|
450
|
+
# <%=
|
451
|
+
# # ...
|
452
|
+
# hidden_field :customer_id
|
453
|
+
# %>
|
454
|
+
#
|
455
|
+
# # Output:
|
456
|
+
# # <input type="hidden" name="delivery[customer_id]" id="delivery-customer-id" value="">
|
457
|
+
def hidden_field(name, attributes = {})
|
458
|
+
input _attributes(:hidden, name, attributes)
|
459
|
+
end
|
460
|
+
|
461
|
+
# File input
|
462
|
+
#
|
463
|
+
# PLEASE REMEMBER TO ADD <tt>enctype: 'multipart/form-data'</tt> ATTRIBUTE TO THE FORM
|
464
|
+
#
|
465
|
+
# @param name [Symbol] the input name
|
466
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
467
|
+
# @option attributes [String,Array] :accept Optional set of accepted MIME Types
|
468
|
+
#
|
469
|
+
# @since x.x.x
|
470
|
+
#
|
471
|
+
# @example Basic usage
|
472
|
+
# <%=
|
473
|
+
# # ...
|
474
|
+
# file_field :avatar
|
475
|
+
# %>
|
476
|
+
#
|
477
|
+
# # Output:
|
478
|
+
# # <input type="file" name="user[avatar]" id="user-avatar">
|
479
|
+
#
|
480
|
+
# @example Accepted mime types
|
481
|
+
# <%=
|
482
|
+
# # ...
|
483
|
+
# file_field :resume, accept: 'application/pdf,application/ms-word'
|
484
|
+
# %>
|
485
|
+
#
|
486
|
+
# # Output:
|
487
|
+
# # <input type="file" name="user[resume]" id="user-resume" accept="application/pdf,application/ms-word">
|
488
|
+
#
|
489
|
+
# @example Accepted mime types (as array)
|
490
|
+
# <%=
|
491
|
+
# # ...
|
492
|
+
# file_field :resume, accept: ['application/pdf', 'application/ms-word']
|
493
|
+
# %>
|
494
|
+
#
|
495
|
+
# # Output:
|
496
|
+
# # <input type="file" name="user[resume]" id="user-resume" accept="application/pdf,application/ms-word">
|
497
|
+
def file_field(name, attributes = {})
|
498
|
+
attributes[:accept] = Array(attributes[:accept]).join(ACCEPT_SEPARATOR) if attributes.key?(:accept)
|
499
|
+
attributes = { type: :file, name: _input_name(name), id: _input_id(name) }.merge(attributes)
|
500
|
+
|
501
|
+
input(attributes)
|
502
|
+
end
|
503
|
+
|
504
|
+
# Text input
|
505
|
+
#
|
506
|
+
# @param name [Symbol] the input name
|
507
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
508
|
+
#
|
509
|
+
# @since x.x.x
|
510
|
+
#
|
511
|
+
# @example Basic usage
|
512
|
+
# <%=
|
513
|
+
# # ...
|
514
|
+
# text_field :first_name
|
515
|
+
# %>
|
516
|
+
#
|
517
|
+
# # Output:
|
518
|
+
# # <input type="text" name="user[first_name]" id="user-first-name" value="">
|
519
|
+
def text_field(name, attributes = {})
|
520
|
+
input _attributes(:text, name, attributes)
|
521
|
+
end
|
522
|
+
alias_method :input_text, :text_field
|
523
|
+
|
524
|
+
# Radio input
|
525
|
+
#
|
526
|
+
# If request params have a value that corresponds to the given value,
|
527
|
+
# it automatically sets the <tt>checked</tt> attribute.
|
528
|
+
# This Lotus::Controller integration happens without any developer intervention.
|
529
|
+
#
|
530
|
+
# @param name [Symbol] the input name
|
531
|
+
# @param value [String] the input value
|
532
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
533
|
+
#
|
534
|
+
# @since x.x.x
|
535
|
+
#
|
536
|
+
# @example Basic usage
|
537
|
+
# <%=
|
538
|
+
# # ...
|
539
|
+
# radio_button :category, 'Fiction'
|
540
|
+
# radio_button :category, 'Non-Fiction'
|
541
|
+
# %>
|
542
|
+
#
|
543
|
+
# # Output:
|
544
|
+
# # <input type="radio" name="book[category]" value="Fiction">
|
545
|
+
# # <input type="radio" name="book[category]" value="Non-Fiction">
|
546
|
+
#
|
547
|
+
# @example Automatic checked value
|
548
|
+
# # Given the following params:
|
549
|
+
# #
|
550
|
+
# # book: {
|
551
|
+
# # category: 'Non-Fiction'
|
552
|
+
# # }
|
553
|
+
#
|
554
|
+
# <%=
|
555
|
+
# # ...
|
556
|
+
# radio_button :category, 'Fiction'
|
557
|
+
# radio_button :category, 'Non-Fiction'
|
558
|
+
# %>
|
559
|
+
#
|
560
|
+
# # Output:
|
561
|
+
# # <input type="radio" name="book[category]" value="Fiction">
|
562
|
+
# # <input type="radio" name="book[category]" value="Non-Fiction" checked="checked">
|
563
|
+
def radio_button(name, value, attributes = {})
|
564
|
+
attributes = { type: :radio, name: _input_name(name), value: value }.merge(attributes)
|
565
|
+
attributes[:checked] = CHECKED if _value(name) == value
|
566
|
+
input(attributes)
|
567
|
+
end
|
568
|
+
|
569
|
+
# Password input
|
570
|
+
#
|
571
|
+
# @param name [Symbol] the input name
|
572
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
573
|
+
#
|
574
|
+
# @since x.x.x
|
575
|
+
#
|
576
|
+
# @example Basic usage
|
577
|
+
# <%=
|
578
|
+
# # ...
|
579
|
+
# password_field :password
|
580
|
+
# %>
|
581
|
+
#
|
582
|
+
# # Output:
|
583
|
+
# # <input type="password" name="signup[password]" id="signup-password" value="">
|
584
|
+
def password_field(name, attributes = {})
|
585
|
+
input({ type: :password, name: _input_name(name), id: _input_id(name), value: nil }.merge(attributes))
|
586
|
+
end
|
587
|
+
|
588
|
+
# Select input
|
589
|
+
#
|
590
|
+
# @param name [Symbol] the input name
|
591
|
+
# @param values [Hash] a Hash to generate <tt><option></tt> tags.
|
592
|
+
# Keys correspond to <tt>value</tt> and values correspond to the content.
|
593
|
+
# @param attributes [Hash] HTML attributes to pass to the input tag
|
594
|
+
#
|
595
|
+
# If request params have a value that corresponds to one of the given values,
|
596
|
+
# it automatically sets the <tt>selected</tt> attribute on the <tt><option></tt> tag.
|
597
|
+
# This Lotus::Controller integration happens without any developer intervention.
|
598
|
+
#
|
599
|
+
# @since x.x.x
|
600
|
+
#
|
601
|
+
# @example Basic usage
|
602
|
+
# <%=
|
603
|
+
# # ...
|
604
|
+
# values = Hash['it' => 'Italy', 'us' => 'United States']
|
605
|
+
# select :stores, values
|
606
|
+
# %>
|
607
|
+
#
|
608
|
+
# # Output:
|
609
|
+
# # <select name="book[store]" id="book-store">
|
610
|
+
# # <option value="it">Italy</option>
|
611
|
+
# # <option value="us">United States</option>
|
612
|
+
# # </select>
|
613
|
+
#
|
614
|
+
# @example Automatic selected option
|
615
|
+
# # Given the following params:
|
616
|
+
# #
|
617
|
+
# # book: {
|
618
|
+
# # store: 'it'
|
619
|
+
# # }
|
620
|
+
#
|
621
|
+
# <%=
|
622
|
+
# # ...
|
623
|
+
# values = Hash['it' => 'Italy', 'us' => 'United States']
|
624
|
+
# select :stores, values
|
625
|
+
# %>
|
626
|
+
#
|
627
|
+
# # Output:
|
628
|
+
# # <select name="book[store]" id="book-store">
|
629
|
+
# # <option value="it" selected="selected">Italy</option>
|
630
|
+
# # <option value="us">United States</option>
|
631
|
+
# # </select>
|
632
|
+
def select(name, values, attributes = {})
|
633
|
+
options = attributes.delete(:options) || {}
|
634
|
+
attributes = { name: _input_name(name), id: _input_id(name) }.merge(attributes)
|
635
|
+
|
636
|
+
super(attributes) do
|
637
|
+
values.each do |value, content|
|
638
|
+
if _value(name) == value
|
639
|
+
option(content, {value: value, selected: SELECTED}.merge(options))
|
640
|
+
else
|
641
|
+
option(content, {value: value}.merge(options))
|
642
|
+
end
|
643
|
+
end
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
# Submit button
|
648
|
+
#
|
649
|
+
# @param content [String] The content
|
650
|
+
# @param attributes [Hash] HTML attributes to pass to the button tag
|
651
|
+
#
|
652
|
+
# @since x.x.x
|
653
|
+
#
|
654
|
+
# @example Basic usage
|
655
|
+
# <%=
|
656
|
+
# # ...
|
657
|
+
# submit 'Create'
|
658
|
+
# %>
|
659
|
+
#
|
660
|
+
# # Output:
|
661
|
+
# # <button type="submit">Create</button>
|
662
|
+
def submit(content, attributes = {})
|
663
|
+
attributes = { type: :submit }.merge(attributes)
|
664
|
+
button(content, attributes)
|
665
|
+
end
|
666
|
+
|
667
|
+
protected
|
668
|
+
# A set of options to pass to the sub form helpers.
|
669
|
+
#
|
670
|
+
# @api private
|
671
|
+
# @since x.x.x
|
672
|
+
def options
|
673
|
+
Hash[name: @name, values: @values, verb: @verb, csrf_token: @csrf_token]
|
674
|
+
end
|
675
|
+
|
676
|
+
private
|
677
|
+
# Check the current builder is top-level
|
678
|
+
#
|
679
|
+
# @api private
|
680
|
+
# @since x.x.x
|
681
|
+
def toplevel?
|
682
|
+
@attributes.any?
|
683
|
+
end
|
684
|
+
|
685
|
+
# Prepare for method override
|
686
|
+
#
|
687
|
+
# @api private
|
688
|
+
# @since x.x.x
|
689
|
+
def _method_override!
|
690
|
+
verb = (@attributes.fetch(:method) { DEFAULT_METHOD }).to_s.upcase
|
691
|
+
|
692
|
+
if BROWSER_METHODS.include?(verb)
|
693
|
+
@attributes[:method] = verb
|
694
|
+
else
|
695
|
+
@attributes[:method] = DEFAULT_METHOD
|
696
|
+
@verb = verb
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
# Return CSRF Protection token from view context
|
701
|
+
#
|
702
|
+
# @api private
|
703
|
+
# @since x.x.x
|
704
|
+
def csrf_token
|
705
|
+
@context.csrf_token if @context.respond_to?(:csrf_token)
|
706
|
+
end
|
707
|
+
|
708
|
+
# Return a set of default HTML attributes
|
709
|
+
#
|
710
|
+
# @api private
|
711
|
+
# @since x.x.x
|
712
|
+
def _attributes(type, name, attributes)
|
713
|
+
{ type: type, name: _input_name(name), id: _input_id(name), value: _value(name) }.merge(attributes)
|
714
|
+
end
|
715
|
+
|
716
|
+
# Input <tt>name</tt> HTML attribute
|
717
|
+
#
|
718
|
+
# @api private
|
719
|
+
# @since x.x.x
|
720
|
+
def _input_name(name)
|
721
|
+
"#{ @name }[#{ name }]"
|
722
|
+
end
|
723
|
+
|
724
|
+
# Input <tt>id</tt> HTML attribute
|
725
|
+
#
|
726
|
+
# @api private
|
727
|
+
# @since x.x.x
|
728
|
+
def _input_id(name)
|
729
|
+
name = _input_name(name).gsub(/\[(?<token>[[[:word:]]\-]*)\]/, INPUT_ID_REPLACEMENT)
|
730
|
+
Utils::String.new(name).dasherize
|
731
|
+
end
|
732
|
+
|
733
|
+
# Input <tt>value</tt> HTML attribute
|
734
|
+
#
|
735
|
+
# @api private
|
736
|
+
# @since x.x.x
|
737
|
+
def _value(name)
|
738
|
+
name = _input_name(name).gsub(/\[(?<token>[[:word:]]*)\]/, INPUT_VALUE_REPLACEMENT)
|
739
|
+
@values.get(name)
|
740
|
+
end
|
741
|
+
|
742
|
+
# Input <tt>for</tt> HTML attribute
|
743
|
+
#
|
744
|
+
# @api private
|
745
|
+
# @since x.x.x
|
746
|
+
def _for(content, name)
|
747
|
+
_input_id(name || content)
|
748
|
+
end
|
749
|
+
|
750
|
+
# Hidden field for check box
|
751
|
+
#
|
752
|
+
# @api private
|
753
|
+
# @since x.x.x
|
754
|
+
#
|
755
|
+
# @see Lotus::Helpers::FormHelper::FormBuilder#check_box
|
756
|
+
def _hidden_field_for_check_box(name, attributes)
|
757
|
+
if attributes[:value].nil? || !attributes[:unchecked_value].nil?
|
758
|
+
input({
|
759
|
+
type: :hidden,
|
760
|
+
name: attributes[:name] || _input_name(name),
|
761
|
+
value: attributes.delete(:unchecked_value) || DEFAULT_UNCHECKED_VALUE
|
762
|
+
})
|
763
|
+
end
|
764
|
+
end
|
765
|
+
|
766
|
+
# HTML attributes for check box
|
767
|
+
#
|
768
|
+
# @api private
|
769
|
+
# @since x.x.x
|
770
|
+
#
|
771
|
+
# @see Lotus::Helpers::FormHelper::FormBuilder#check_box
|
772
|
+
def _attributes_for_check_box(name, attributes)
|
773
|
+
attributes = {
|
774
|
+
type: :checkbox,
|
775
|
+
name: _input_name(name),
|
776
|
+
id: _input_id(name),
|
777
|
+
value: attributes.delete(:checked_value) || DEFAULT_CHECKED_VALUE
|
778
|
+
}.merge(attributes)
|
779
|
+
|
780
|
+
value = _value(name)
|
781
|
+
attributes[:checked] = CHECKED if value &&
|
782
|
+
( value == attributes[:value] || value.include?(attributes[:value]) )
|
783
|
+
|
784
|
+
attributes
|
785
|
+
end
|
786
|
+
end
|
787
|
+
end
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|