form_props 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +714 -0
- data/lib/form_props/action_view_extensions/form_helper.rb +120 -0
- data/lib/form_props/form_builder.rb +223 -0
- data/lib/form_props/form_options_helper.rb +158 -0
- data/lib/form_props/inputs/base.rb +164 -0
- data/lib/form_props/inputs/check_box.rb +69 -0
- data/lib/form_props/inputs/collection_check_boxes.rb +36 -0
- data/lib/form_props/inputs/collection_helpers.rb +53 -0
- data/lib/form_props/inputs/collection_radio_buttons.rb +35 -0
- data/lib/form_props/inputs/collection_select.rb +30 -0
- data/lib/form_props/inputs/color_field.rb +27 -0
- data/lib/form_props/inputs/date_field.rb +17 -0
- data/lib/form_props/inputs/datetime_field.rb +32 -0
- data/lib/form_props/inputs/datetime_local_field.rb +17 -0
- data/lib/form_props/inputs/email_field.rb +13 -0
- data/lib/form_props/inputs/file_field.rb +13 -0
- data/lib/form_props/inputs/grouped_collection_select.rb +31 -0
- data/lib/form_props/inputs/hidden_field.rb +16 -0
- data/lib/form_props/inputs/month_field.rb +17 -0
- data/lib/form_props/inputs/number_field.rb +21 -0
- data/lib/form_props/inputs/password_field.rb +18 -0
- data/lib/form_props/inputs/radio_button.rb +48 -0
- data/lib/form_props/inputs/range_field.rb +13 -0
- data/lib/form_props/inputs/search_field.rb +28 -0
- data/lib/form_props/inputs/select.rb +42 -0
- data/lib/form_props/inputs/submit.rb +26 -0
- data/lib/form_props/inputs/tel_field.rb +13 -0
- data/lib/form_props/inputs/text_area.rb +37 -0
- data/lib/form_props/inputs/text_field.rb +28 -0
- data/lib/form_props/inputs/time_field.rb +17 -0
- data/lib/form_props/inputs/time_zone_select.rb +22 -0
- data/lib/form_props/inputs/url_field.rb +13 -0
- data/lib/form_props/inputs/week_field.rb +17 -0
- data/lib/form_props/inputs/weekday_select.rb +28 -0
- data/lib/form_props/version.rb +5 -0
- data/lib/form_props.rb +45 -0
- metadata +120 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class CheckBox < Base
|
6
|
+
def initialize(object_name, method_name, template_object, checked_value, unchecked_value, options)
|
7
|
+
@checked_value = checked_value
|
8
|
+
@unchecked_value = unchecked_value
|
9
|
+
super(object_name, method_name, template_object, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def input_checked?(options)
|
13
|
+
if options.has_key?(:checked)
|
14
|
+
checked = options.delete(:checked)
|
15
|
+
checked == true || checked == "checked"
|
16
|
+
else
|
17
|
+
checked?(value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def render(flatten = false)
|
22
|
+
options = @options.stringify_keys
|
23
|
+
options[:type] = "checkbox"
|
24
|
+
options[:value] = @checked_value
|
25
|
+
options[:checked] = true if input_checked?(options)
|
26
|
+
options[:unchecked_value] = @unchecked_value || ""
|
27
|
+
options[:include_hidden] = options.fetch(:include_hidden) { true }
|
28
|
+
|
29
|
+
body_block = -> {
|
30
|
+
if options[:multiple]
|
31
|
+
add_default_name_and_id_for_value(@checked_value, options)
|
32
|
+
options.delete(:multiple)
|
33
|
+
else
|
34
|
+
add_default_name_and_id(options)
|
35
|
+
end
|
36
|
+
|
37
|
+
input_props(options)
|
38
|
+
}
|
39
|
+
|
40
|
+
if flatten
|
41
|
+
body_block.call
|
42
|
+
else
|
43
|
+
json.set!(sanitized_method_name) do
|
44
|
+
body_block.call
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def checked?(value)
|
52
|
+
case value
|
53
|
+
when TrueClass, FalseClass
|
54
|
+
value == !!@checked_value
|
55
|
+
when NilClass
|
56
|
+
false
|
57
|
+
when String
|
58
|
+
value == @checked_value
|
59
|
+
else
|
60
|
+
if value.respond_to?(:include?)
|
61
|
+
value.include?(@checked_value)
|
62
|
+
else
|
63
|
+
value.to_i == @checked_value.to_i
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_view/helpers/tags/collection_helpers"
|
4
|
+
|
5
|
+
module FormProps
|
6
|
+
module Inputs
|
7
|
+
class CollectionCheckBoxes < Base
|
8
|
+
include ActionView::Helpers::Tags::CollectionHelpers
|
9
|
+
include CollectionHelpers
|
10
|
+
|
11
|
+
class CheckBoxBuilder < Builder
|
12
|
+
def render(extra_html_options = {})
|
13
|
+
html_options = extra_html_options.merge(@input_html_options)
|
14
|
+
html_options[:multiple] = true
|
15
|
+
html_options[:skip_default_ids] = false
|
16
|
+
|
17
|
+
checkbox = CheckBox.new(@object_name, @method_name, @template_object, @value, nil, html_options)
|
18
|
+
checkbox.render(true)
|
19
|
+
checkbox.json.label @text
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def render
|
24
|
+
json.set!(sanitized_method_name) do
|
25
|
+
json.collection do
|
26
|
+
render_collection_for(CheckBoxBuilder)
|
27
|
+
end
|
28
|
+
|
29
|
+
json.include_hidden(@options.fetch(:include_hidden) { true })
|
30
|
+
|
31
|
+
input_props(@html_options)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
module CollectionHelpers
|
6
|
+
def render_collection
|
7
|
+
json.array! @collection do |item|
|
8
|
+
value = value_for_collection(item, @value_method)
|
9
|
+
text = value_for_collection(item, @text_method)
|
10
|
+
default_html_options = default_html_options_for_collection(item, value)
|
11
|
+
additional_html_options = option_html_attributes(item)
|
12
|
+
|
13
|
+
yield item, value, text, default_html_options.merge(additional_html_options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def default_html_options_for_collection(item, value)
|
18
|
+
html_options = @html_options.dup
|
19
|
+
|
20
|
+
[:checked, :selected, :disabled, :read_only].each do |option|
|
21
|
+
current_value = @options[option]
|
22
|
+
next if current_value.nil?
|
23
|
+
|
24
|
+
accept = if current_value.respond_to?(:call)
|
25
|
+
current_value.call(item)
|
26
|
+
else
|
27
|
+
Array(current_value).map(&:to_s).include?(value.to_s)
|
28
|
+
end
|
29
|
+
|
30
|
+
if accept
|
31
|
+
html_options[option] = true
|
32
|
+
elsif option == :checked
|
33
|
+
html_options[option] = false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
html_options[:object] = @object
|
38
|
+
html_options
|
39
|
+
end
|
40
|
+
|
41
|
+
def render_collection_for(builder_class, &block)
|
42
|
+
render_collection do |item, value, text, default_html_options|
|
43
|
+
builder = instantiate_builder(builder_class, item, value, text, default_html_options)
|
44
|
+
builder.render
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def hidden_field_name
|
49
|
+
@html_options[:name] || tag_name(false, @options[:index]).to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_view/helpers/tags/collection_helpers"
|
4
|
+
|
5
|
+
module FormProps
|
6
|
+
module Inputs
|
7
|
+
class CollectionRadioButtons < Base
|
8
|
+
include ActionView::Helpers::Tags::CollectionHelpers
|
9
|
+
include CollectionHelpers
|
10
|
+
|
11
|
+
class RadioButtonBuilder < Builder
|
12
|
+
def render(extra_html_options = {})
|
13
|
+
html_options = extra_html_options.merge(@input_html_options)
|
14
|
+
html_options[:skip_default_ids] = false
|
15
|
+
|
16
|
+
checkbox = RadioButton.new(@object_name, @method_name, @template_object, @value, html_options)
|
17
|
+
checkbox.render(true)
|
18
|
+
checkbox.json.label @text
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def render
|
23
|
+
json.set!(sanitized_method_name) do
|
24
|
+
json.collection do
|
25
|
+
render_collection_for(RadioButtonBuilder)
|
26
|
+
end
|
27
|
+
|
28
|
+
json.include_hidden(@options.fetch(:include_hidden) { true })
|
29
|
+
|
30
|
+
input_props(@html_options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class CollectionSelect < Base # :nodoc:
|
6
|
+
include FormOptionsHelper
|
7
|
+
|
8
|
+
def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
|
9
|
+
@collection = collection
|
10
|
+
@value_method = value_method
|
11
|
+
@text_method = text_method
|
12
|
+
@html_options = html_options
|
13
|
+
|
14
|
+
super(object_name, method_name, template_object, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def render
|
18
|
+
option_tags_options = {
|
19
|
+
selected: @options.fetch(:selected) { value },
|
20
|
+
disabled: @options[:disabled]
|
21
|
+
}
|
22
|
+
|
23
|
+
select_content_props(
|
24
|
+
options_from_collection_for_select(@collection, @value_method, @text_method, option_tags_options),
|
25
|
+
@options, @html_options
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class ColorField < TextField
|
6
|
+
def render
|
7
|
+
@options["value"] ||= validate_color_string(value)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def validate_color_string(string)
|
14
|
+
regex = /#[0-9a-fA-F]{6}/
|
15
|
+
if regex.match?(string)
|
16
|
+
string.downcase
|
17
|
+
else
|
18
|
+
"#000000"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def field_type
|
23
|
+
"color"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class DatetimeField < TextField
|
6
|
+
def render
|
7
|
+
@options[:value] ||= format_date(value)
|
8
|
+
@options[:min] = format_date(datetime_value(@options["min"]))
|
9
|
+
@options[:max] = format_date(datetime_value(@options["max"]))
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def format_date(value)
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
def datetime_value(value)
|
20
|
+
if value.is_a? String
|
21
|
+
begin
|
22
|
+
DateTime.parse(value)
|
23
|
+
rescue
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
else
|
27
|
+
value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class DatetimeLocalField < DatetimeField
|
6
|
+
private
|
7
|
+
|
8
|
+
def format_date(value)
|
9
|
+
value&.strftime("%Y-%m-%dT%T")
|
10
|
+
end
|
11
|
+
|
12
|
+
def field_type
|
13
|
+
"datetime-local"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class GroupedCollectionSelect < Base
|
6
|
+
include FormOptionsHelper
|
7
|
+
|
8
|
+
def initialize(object_name, method_name, template_object, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
9
|
+
@collection = collection
|
10
|
+
@group_method = group_method
|
11
|
+
@group_label_method = group_label_method
|
12
|
+
@option_key_method = option_key_method
|
13
|
+
@option_value_method = option_value_method
|
14
|
+
@html_options = html_options
|
15
|
+
|
16
|
+
super(object_name, method_name, template_object, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def render
|
20
|
+
option_tags_options = {
|
21
|
+
selected: @options.fetch(:selected) { value },
|
22
|
+
disabled: @options[:disabled]
|
23
|
+
}
|
24
|
+
|
25
|
+
select_content_props(
|
26
|
+
option_groups_from_collection_for_select(@collection, @group_method, @group_label_method, @option_key_method, @option_value_method, option_tags_options), @options, @html_options
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class NumberField < TextField
|
6
|
+
def render
|
7
|
+
if (range = @options.delete("in") || @options.delete("within"))
|
8
|
+
@options.update("min" => range.min, "max" => range.max)
|
9
|
+
end
|
10
|
+
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def field_type
|
17
|
+
"number"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class PasswordField < TextField
|
6
|
+
def render
|
7
|
+
@options = {value: nil}.merge!(@options)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def field_type
|
14
|
+
"password"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class RadioButton < Base
|
6
|
+
def initialize(object_name, method_name, template_object, tag_value, options)
|
7
|
+
@tag_value = tag_value
|
8
|
+
super(object_name, method_name, template_object, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def input_checked?(options)
|
12
|
+
if options.has_key?(:checked)
|
13
|
+
checked = options.delete(:checked)
|
14
|
+
checked == true || checked == "checked"
|
15
|
+
else
|
16
|
+
checked?(value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(flatten = false)
|
21
|
+
@options[:type] = "radio"
|
22
|
+
@options[:value] = @tag_value
|
23
|
+
@options[:checked] = true if input_checked?(@options)
|
24
|
+
|
25
|
+
name_for_key = sanitized_method_name + "_#{sanitized_value(@tag_value)}"
|
26
|
+
|
27
|
+
body_block = -> {
|
28
|
+
add_default_name_and_id_for_value(@tag_value, @options)
|
29
|
+
input_props(@options)
|
30
|
+
}
|
31
|
+
|
32
|
+
if flatten
|
33
|
+
body_block.call
|
34
|
+
else
|
35
|
+
json.set!(name_for_key) do
|
36
|
+
body_block.call
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def checked?(value)
|
44
|
+
value.to_s == @tag_value.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class SearchField < FormProps::Inputs::TextField
|
6
|
+
def render
|
7
|
+
if @options[:autosave]
|
8
|
+
if @options[:autosave] == true
|
9
|
+
@options[:autosave] = request.host.split(".").reverse.join(".")
|
10
|
+
end
|
11
|
+
@options[:results] ||= 10
|
12
|
+
end
|
13
|
+
|
14
|
+
if @options[:onsearch]
|
15
|
+
@options[:incremental] = true unless @options.has_key?(:incremental)
|
16
|
+
end
|
17
|
+
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def field_type
|
24
|
+
"search"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class Select < Base
|
6
|
+
include FormOptionsHelper
|
7
|
+
|
8
|
+
def initialize(object_name, method_name, template_object, choices, options, html_options)
|
9
|
+
@choices = choices
|
10
|
+
@choices = @choices.to_a if @choices.is_a?(Range)
|
11
|
+
|
12
|
+
@html_options = html_options
|
13
|
+
super(object_name, method_name, template_object, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def render
|
17
|
+
option_tags_options = {
|
18
|
+
selected: @options.fetch(:selected) { value.nil? ? "" : value },
|
19
|
+
disabled: @options[:disabled]
|
20
|
+
}
|
21
|
+
|
22
|
+
option_tags = if grouped_choices?
|
23
|
+
grouped_options_for_select(@choices, option_tags_options)
|
24
|
+
else
|
25
|
+
options_for_select(@choices, option_tags_options)
|
26
|
+
end
|
27
|
+
|
28
|
+
select_content_props(option_tags, @options, @html_options)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Grouped choices look like this:
|
34
|
+
#
|
35
|
+
# [nil, []]
|
36
|
+
# { nil => [] }
|
37
|
+
def grouped_choices?
|
38
|
+
!@choices.blank? && @choices.first.respond_to?(:last) && Array === @choices.first.last
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FormProps
|
4
|
+
module Inputs
|
5
|
+
class Submit < Base
|
6
|
+
def initialize(template_object, options)
|
7
|
+
@template_object = template_object
|
8
|
+
@options = options.with_indifferent_access
|
9
|
+
end
|
10
|
+
|
11
|
+
def render
|
12
|
+
@options[:type] = field_type
|
13
|
+
|
14
|
+
json.set!(:submit) do
|
15
|
+
input_props(@options)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def field_type
|
22
|
+
"submit"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_view/helpers/tags/placeholderable"
|
4
|
+
|
5
|
+
module FormProps
|
6
|
+
module Inputs
|
7
|
+
class TextArea < Base
|
8
|
+
include ActionView::Helpers::Tags::Placeholderable
|
9
|
+
|
10
|
+
def render
|
11
|
+
json.set!(sanitized_method_name) do
|
12
|
+
add_default_name_and_id(@options)
|
13
|
+
@options[:type] ||= field_type
|
14
|
+
@options[:value] = @options.fetch(:value) { value_before_type_cast }
|
15
|
+
|
16
|
+
if (size = @options.delete(:size))
|
17
|
+
@options[:cols], @options[:rows] = size.split("x") if size.respond_to?(:split)
|
18
|
+
end
|
19
|
+
|
20
|
+
input_props(@options)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def field_type
|
26
|
+
@field_type ||= name.split("::").last.sub("Field", "").downcase
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def field_type
|
33
|
+
self.class.field_type
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|