form_props 0.0.1
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 +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
|