phlexi-display 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/.rspec +3 -0
- data/.ruby-version +1 -0
- data/Appraisals +8 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +13 -0
- data/Rakefile +14 -0
- data/TODO +0 -0
- data/config.ru +6 -0
- data/gemfiles/default.gemfile +5 -0
- data/gemfiles/rails_7.gemfile +8 -0
- data/lib/phlexi/display/base.rb +243 -0
- data/lib/phlexi/display/components/base.rb +51 -0
- data/lib/phlexi/display/components/checkbox.rb +48 -0
- data/lib/phlexi/display/components/collection_checkboxes.rb +44 -0
- data/lib/phlexi/display/components/collection_radio_buttons.rb +35 -0
- data/lib/phlexi/display/components/concerns/handles_array_input.rb +21 -0
- data/lib/phlexi/display/components/concerns/handles_input.rb +53 -0
- data/lib/phlexi/display/components/concerns/has_options.rb +37 -0
- data/lib/phlexi/display/components/concerns/submits_form.rb +39 -0
- data/lib/phlexi/display/components/error.rb +21 -0
- data/lib/phlexi/display/components/file_input.rb +32 -0
- data/lib/phlexi/display/components/full_error.rb +21 -0
- data/lib/phlexi/display/components/hint.rb +21 -0
- data/lib/phlexi/display/components/input.rb +84 -0
- data/lib/phlexi/display/components/input_array.rb +45 -0
- data/lib/phlexi/display/components/label.rb +27 -0
- data/lib/phlexi/display/components/radio_button.rb +41 -0
- data/lib/phlexi/display/components/select.rb +69 -0
- data/lib/phlexi/display/components/submit_button.rb +41 -0
- data/lib/phlexi/display/components/textarea.rb +34 -0
- data/lib/phlexi/display/components/wrapper.rb +31 -0
- data/lib/phlexi/display/field_options/associations.rb +21 -0
- data/lib/phlexi/display/field_options/autofocus.rb +18 -0
- data/lib/phlexi/display/field_options/collection.rb +54 -0
- data/lib/phlexi/display/field_options/disabled.rb +18 -0
- data/lib/phlexi/display/field_options/errors.rb +92 -0
- data/lib/phlexi/display/field_options/hints.rb +22 -0
- data/lib/phlexi/display/field_options/inferred_types.rb +155 -0
- data/lib/phlexi/display/field_options/labels.rb +28 -0
- data/lib/phlexi/display/field_options/length.rb +53 -0
- data/lib/phlexi/display/field_options/limit.rb +66 -0
- data/lib/phlexi/display/field_options/min_max.rb +92 -0
- data/lib/phlexi/display/field_options/multiple.rb +65 -0
- data/lib/phlexi/display/field_options/pattern.rb +38 -0
- data/lib/phlexi/display/field_options/placeholder.rb +18 -0
- data/lib/phlexi/display/field_options/readonly.rb +18 -0
- data/lib/phlexi/display/field_options/required.rb +37 -0
- data/lib/phlexi/display/field_options/themes.rb +207 -0
- data/lib/phlexi/display/field_options/validators.rb +48 -0
- data/lib/phlexi/display/option_mapper.rb +154 -0
- data/lib/phlexi/display/structure/dom.rb +62 -0
- data/lib/phlexi/display/structure/field_builder.rb +236 -0
- data/lib/phlexi/display/structure/field_collection.rb +54 -0
- data/lib/phlexi/display/structure/namespace.rb +135 -0
- data/lib/phlexi/display/structure/namespace_collection.rb +48 -0
- data/lib/phlexi/display/structure/node.rb +18 -0
- data/lib/phlexi/display/version.rb +7 -0
- data/lib/phlexi/display.rb +31 -0
- data/lib/phlexi-display.rb +3 -0
- data/sig/phlexi/display.rbs +6 -0
- metadata +262 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class Error < Base
|
7
|
+
def view_template
|
8
|
+
p(**attributes) do
|
9
|
+
field.error
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def render?
|
16
|
+
field.show_errors? && field.has_errors?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class FileInput < Input
|
7
|
+
def view_template
|
8
|
+
input(type: :hidden, name: attributes[:name], value: "", autocomplete: "off", hidden: true) if include_hidden?
|
9
|
+
input(**attributes)
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def build_input_attributes
|
15
|
+
attributes[:type] = :file
|
16
|
+
super
|
17
|
+
attributes[:value] = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def include_hidden?
|
21
|
+
return false if @include_hidden == false
|
22
|
+
|
23
|
+
attributes[:multiple]
|
24
|
+
end
|
25
|
+
|
26
|
+
def normalize_input(input_value)
|
27
|
+
input_value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class FullError < Base
|
7
|
+
def view_template
|
8
|
+
p(**attributes) do
|
9
|
+
field.full_error
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def render?
|
16
|
+
field.show_errors? && field.has_errors?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class Hint < Base
|
7
|
+
def view_template
|
8
|
+
p(**attributes) do
|
9
|
+
field.hint
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def render?
|
16
|
+
field.hint.present? && (!field.show_errors? || !field.has_errors?)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class Input < Base
|
7
|
+
include Concerns::HandlesInput
|
8
|
+
|
9
|
+
def view_template
|
10
|
+
input(**attributes)
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def build_attributes
|
16
|
+
super
|
17
|
+
|
18
|
+
# only overwrite id if it was set in Base
|
19
|
+
attributes[:id] = field.dom.id if attributes[:id] == "#{field.dom.id}_#{component_name}"
|
20
|
+
|
21
|
+
attributes[:name] = field.dom.name
|
22
|
+
attributes[:value] = field.dom.value
|
23
|
+
|
24
|
+
build_input_attributes
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_input_attributes
|
28
|
+
attributes.fetch(:type) { attributes[:type] = field.inferred_input_type }
|
29
|
+
attributes.fetch(:disabled) { attributes[:disabled] = field.disabled? }
|
30
|
+
|
31
|
+
case attributes[:type]
|
32
|
+
when :text, :password, :email, :tel, :url, :search
|
33
|
+
attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
|
34
|
+
attributes.fetch(:placeholder) { attributes[:placeholder] = field.placeholder }
|
35
|
+
attributes.fetch(:minlength) { attributes[:minlength] = field.minlength }
|
36
|
+
attributes.fetch(:maxlength) { attributes[:maxlength] = field.maxlength }
|
37
|
+
attributes.fetch(:readonly) { attributes[:readonly] = field.readonly? }
|
38
|
+
attributes.fetch(:required) { attributes[:required] = field.required? }
|
39
|
+
attributes.fetch(:pattern) { attributes[:pattern] = field.pattern }
|
40
|
+
when :number
|
41
|
+
attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
|
42
|
+
attributes.fetch(:placeholder) { attributes[:placeholder] = field.placeholder }
|
43
|
+
attributes.fetch(:readonly) { attributes[:readonly] = field.readonly? }
|
44
|
+
attributes.fetch(:required) { attributes[:required] = field.required? }
|
45
|
+
attributes.fetch(:min) { attributes[:min] = field.min }
|
46
|
+
attributes.fetch(:max) { attributes[:max] = field.max }
|
47
|
+
attributes.fetch(:step) { attributes[:step] = field.step }
|
48
|
+
when :checkbox, :radio
|
49
|
+
attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
|
50
|
+
attributes.fetch(:required) { attributes[:required] = field.required? }
|
51
|
+
when :file
|
52
|
+
attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
|
53
|
+
attributes.fetch(:required) { attributes[:required] = field.required? }
|
54
|
+
attributes.fetch(:multiple) { attributes[:multiple] = field.multiple? }
|
55
|
+
when :date, :time, :datetime_local
|
56
|
+
attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
|
57
|
+
attributes.fetch(:readonly) { attributes[:readonly] = field.readonly? }
|
58
|
+
attributes.fetch(:required) { attributes[:required] = field.required? }
|
59
|
+
attributes.fetch(:min) { attributes[:min] = field.min }
|
60
|
+
attributes.fetch(:max) { attributes[:max] = field.max }
|
61
|
+
when :color
|
62
|
+
attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
|
63
|
+
when :range
|
64
|
+
attributes.fetch(:autofocus) { attributes[:autofocus] = field.focused? }
|
65
|
+
attributes.fetch(:min) { attributes[:min] = field.min }
|
66
|
+
attributes.fetch(:max) { attributes[:max] = field.max }
|
67
|
+
attributes.fetch(:step) { attributes[:step] = field.step }
|
68
|
+
when :hidden
|
69
|
+
attributes[:class] = false
|
70
|
+
attributes[:hidden] = true
|
71
|
+
attributes[:autocomplete] = "off"
|
72
|
+
else
|
73
|
+
# Handle any unrecognized input types
|
74
|
+
# Rails.logger.warn("Unhandled input type: #{attributes[:type]}")
|
75
|
+
end
|
76
|
+
|
77
|
+
if (attributes[:type] == :file) ? attributes[:multiple] : attributes.delete(:multiple)
|
78
|
+
attributes[:name] = "#{attributes[:name].sub(/\[\]$/, "")}[]"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class InputArray < Base
|
7
|
+
include Concerns::HandlesInput
|
8
|
+
include Concerns::HandlesArrayInput
|
9
|
+
|
10
|
+
def view_template
|
11
|
+
div(**attributes.slice(:id, :class)) do
|
12
|
+
field.multi(values.length) do |builder|
|
13
|
+
render builder.hidden_field_tag if builder.index == 0
|
14
|
+
|
15
|
+
field = builder.field(
|
16
|
+
label: builder.key,
|
17
|
+
# we expect key to be an integer string starting from "1"
|
18
|
+
value: values[builder.index]
|
19
|
+
)
|
20
|
+
if block_given?
|
21
|
+
yield field
|
22
|
+
else
|
23
|
+
render field.input_tag
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def build_attributes
|
32
|
+
super
|
33
|
+
|
34
|
+
attributes[:multiple] = true
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def values
|
40
|
+
@values ||= Array(field.value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class Label < Base
|
7
|
+
def view_template
|
8
|
+
label(**attributes) do
|
9
|
+
if field.required?
|
10
|
+
abbr(title: "required") { "*" }
|
11
|
+
whitespace
|
12
|
+
end
|
13
|
+
plain field.label
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def build_attributes
|
20
|
+
super
|
21
|
+
|
22
|
+
attributes.fetch(:for) { attributes[:for] = field.dom.id }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class RadioButton < Input
|
7
|
+
def view_template
|
8
|
+
input(**attributes, value: @checked_value)
|
9
|
+
end
|
10
|
+
|
11
|
+
def extract_input(...)
|
12
|
+
# when a radio is not submitted, nothing is returned
|
13
|
+
super.compact
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def build_input_attributes
|
19
|
+
attributes[:type] = :radio
|
20
|
+
super
|
21
|
+
|
22
|
+
@checked_value = (attributes.key?(:checked_value) ? attributes.delete(:checked_value) : "1").to_s
|
23
|
+
|
24
|
+
# this is a hack to workaround the fact that radio cannot be indexed/multiple
|
25
|
+
attributes[:name] = attributes[:name].sub(/\[\]$/, "")
|
26
|
+
attributes[:value] = @checked_value
|
27
|
+
attributes[:checked] = attributes.fetch(:checked) { checked? }
|
28
|
+
end
|
29
|
+
|
30
|
+
def checked?
|
31
|
+
field.dom.value == @checked_value
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize_input(...)
|
35
|
+
input_value = super
|
36
|
+
(input_value == @checked_value) ? input_value : nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class Select < Base
|
7
|
+
include Concerns::HandlesInput
|
8
|
+
include Concerns::HandlesArrayInput
|
9
|
+
include Concerns::HasOptions
|
10
|
+
|
11
|
+
def view_template
|
12
|
+
PUI::Select.new
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def options
|
18
|
+
option_mapper.each do |value, label|
|
19
|
+
option(selected: selected?(value), value: value) { label }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def blank_option(&)
|
24
|
+
option(selected: field.value.nil?, &)
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_attributes
|
28
|
+
super
|
29
|
+
|
30
|
+
attributes[:id] = field.dom.id
|
31
|
+
attributes[:name] = field.dom.name
|
32
|
+
|
33
|
+
build_select_attributes
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_select_attributes
|
37
|
+
@include_blank = attributes.delete(:include_blank)
|
38
|
+
@include_hidden = attributes.delete(:include_hidden)
|
39
|
+
|
40
|
+
attributes[:autofocus] = attributes.fetch(:autofocus, field.focused?)
|
41
|
+
attributes[:required] = attributes.fetch(:required, field.required?)
|
42
|
+
attributes[:disabled] = attributes.fetch(:disabled, field.disabled?)
|
43
|
+
attributes[:multiple] = attributes.fetch(:multiple, field.multiple?)
|
44
|
+
attributes[:size] = attributes.fetch(:size, field.limit)
|
45
|
+
end
|
46
|
+
|
47
|
+
def blank_option_text
|
48
|
+
field.placeholder
|
49
|
+
end
|
50
|
+
|
51
|
+
def include_blank?
|
52
|
+
return true if @include_blank == true
|
53
|
+
|
54
|
+
@include_blank != false && !attributes[:multiple]
|
55
|
+
end
|
56
|
+
|
57
|
+
def include_hidden?
|
58
|
+
return false if @include_hidden == false
|
59
|
+
|
60
|
+
attributes[:multiple]
|
61
|
+
end
|
62
|
+
|
63
|
+
def normalize_input(input_value)
|
64
|
+
attributes[:multiple] ? normalize_array_input(input_value) : normalize_simple_input(input_value)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class SubmitButton < Base
|
7
|
+
include Concerns::SubmitsDisplay
|
8
|
+
|
9
|
+
def view_template(&content)
|
10
|
+
content ||= proc { submit_type_label }
|
11
|
+
button(**attributes, &content)
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def build_attributes
|
17
|
+
root_key = field.dom.lineage.first.respond_to?(:dom_id) ? field.dom.lineage.first.dom_id : field.dom.lineage.first.key
|
18
|
+
attributes.fetch(:id) { attributes[:id] = "#{root_key}_submit_button" }
|
19
|
+
attributes[:class] = tokens(
|
20
|
+
component_name,
|
21
|
+
submit_type_value,
|
22
|
+
attributes[:class]
|
23
|
+
)
|
24
|
+
|
25
|
+
build_button_attributes
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_button_attributes
|
29
|
+
formmethod = attributes[:formmethod]
|
30
|
+
if formmethod.present? && !/post|get/i.match?(formmethod) && !attributes.key?(:name) && !attributes.key?(:value)
|
31
|
+
attributes.merge! formmethod: :post, name: "_method", value: formmethod
|
32
|
+
end
|
33
|
+
|
34
|
+
attributes.fetch(:name) { attributes[:name] = "commit" }
|
35
|
+
attributes.fetch(:value) { attributes[:value] = submit_type_label }
|
36
|
+
attributes.fetch(:type) { attributes[:type] = :submit }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class Textarea < Base
|
7
|
+
def view_template
|
8
|
+
textarea(**attributes) { field.dom.value }
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def build_attributes
|
14
|
+
super
|
15
|
+
|
16
|
+
attributes[:id] = field.dom.id
|
17
|
+
attributes[:name] = field.dom.name
|
18
|
+
|
19
|
+
build_textarea_attributes
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_textarea_attributes
|
23
|
+
attributes[:placeholder] = attributes.fetch(:placeholder, field.placeholder)
|
24
|
+
attributes[:autofocus] = attributes.fetch(:autofocus, field.focused?)
|
25
|
+
attributes[:minlength] = attributes.fetch(:minlength, field.minlength)
|
26
|
+
attributes[:maxlength] = attributes.fetch(:maxlength, field.maxlength)
|
27
|
+
attributes[:readonly] = attributes.fetch(:readonly, field.readonly?)
|
28
|
+
attributes[:required] = attributes.fetch(:required, field.required?)
|
29
|
+
attributes[:disabled] = attributes.fetch(:disabled, field.disabled?)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module Components
|
6
|
+
class Wrapper < Base
|
7
|
+
attr_reader :inner_attributes
|
8
|
+
|
9
|
+
def view_template
|
10
|
+
div(**attributes) do
|
11
|
+
render field.label_tag
|
12
|
+
div(**inner_attributes) do
|
13
|
+
yield field if block_given?
|
14
|
+
render field.full_error_tag
|
15
|
+
render field.hint_tag
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def build_attributes
|
23
|
+
super
|
24
|
+
|
25
|
+
@inner_attributes = attributes.delete(:inner) || {}
|
26
|
+
inner_attributes[:class] = tokens("inner-wrapper", inner_attributes[:class])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module FieldOptions
|
6
|
+
module Associations
|
7
|
+
protected
|
8
|
+
|
9
|
+
def reflection
|
10
|
+
@reflection ||= find_association_reflection
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_association_reflection
|
14
|
+
if object.class.respond_to?(:reflect_on_association)
|
15
|
+
object.class.reflect_on_association(key)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module FieldOptions
|
6
|
+
module Autofocus
|
7
|
+
def focused?
|
8
|
+
options[:autofocus] == true
|
9
|
+
end
|
10
|
+
|
11
|
+
def focused!(autofocus = true)
|
12
|
+
options[:autofocus] = autofocus
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module FieldOptions
|
6
|
+
module Collection
|
7
|
+
def collection(collection = nil)
|
8
|
+
if collection.nil?
|
9
|
+
options[:collection] = options.fetch(:collection) { infer_collection }
|
10
|
+
else
|
11
|
+
options[:collection] = collection
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def infer_collection
|
19
|
+
collection_value_from_association || collection_value_from_validator
|
20
|
+
end
|
21
|
+
|
22
|
+
def collection_value_from_association
|
23
|
+
return unless reflection
|
24
|
+
|
25
|
+
relation = reflection.klass.all
|
26
|
+
|
27
|
+
if reflection.respond_to?(:scope) && reflection.scope
|
28
|
+
relation = if reflection.scope.parameters.any?
|
29
|
+
reflection.klass.instance_exec(object, &reflection.scope)
|
30
|
+
else
|
31
|
+
reflection.klass.instance_exec(&reflection.scope)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
order = reflection.options[:order]
|
35
|
+
conditions = reflection.options[:conditions]
|
36
|
+
conditions = object.instance_exec(&conditions) if conditions.respond_to?(:call)
|
37
|
+
|
38
|
+
relation = relation.where(conditions) if relation.respond_to?(:where) && conditions.present?
|
39
|
+
relation = relation.order(order) if relation.respond_to?(:order)
|
40
|
+
end
|
41
|
+
|
42
|
+
relation
|
43
|
+
end
|
44
|
+
|
45
|
+
def collection_value_from_validator
|
46
|
+
return unless has_validators?
|
47
|
+
|
48
|
+
inclusion_validator = find_validator(:inclusion)
|
49
|
+
inclusion_validator.options[:in] || inclusion_validator.options[:within] if inclusion_validator
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module FieldOptions
|
6
|
+
module Disabled
|
7
|
+
def disabled?
|
8
|
+
options[:disabled] == true
|
9
|
+
end
|
10
|
+
|
11
|
+
def disabled!(disabled = true)
|
12
|
+
options[:disabled] = disabled
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module FieldOptions
|
6
|
+
module Errors
|
7
|
+
def custom_error(error)
|
8
|
+
options[:error] = error
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def error
|
13
|
+
error_text if has_errors?
|
14
|
+
end
|
15
|
+
|
16
|
+
def full_error
|
17
|
+
full_error_text if has_errors?
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_errors?
|
21
|
+
object_with_errors? || !object && has_custom_error?
|
22
|
+
end
|
23
|
+
|
24
|
+
def show_errors?
|
25
|
+
options[:error] != false
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid?
|
29
|
+
!has_errors? && has_value?
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def error_text
|
35
|
+
text = has_custom_error? ? options[:error] : errors.send(error_method)
|
36
|
+
|
37
|
+
"#{options[:error_prefix]} #{text}".lstrip
|
38
|
+
end
|
39
|
+
|
40
|
+
def full_error_text
|
41
|
+
has_custom_error? ? options[:error] : full_errors.send(error_method)
|
42
|
+
end
|
43
|
+
|
44
|
+
def object_with_errors?
|
45
|
+
object&.respond_to?(:errors) && errors.present?
|
46
|
+
end
|
47
|
+
|
48
|
+
def error_method
|
49
|
+
options[:error_method] || :first
|
50
|
+
end
|
51
|
+
|
52
|
+
def errors
|
53
|
+
@errors ||= (errors_on_attribute + errors_on_association).compact
|
54
|
+
end
|
55
|
+
|
56
|
+
def full_errors
|
57
|
+
@full_errors ||= (full_errors_on_attribute + full_errors_on_association).compact
|
58
|
+
end
|
59
|
+
|
60
|
+
def errors_on_attribute
|
61
|
+
object.errors[key] || []
|
62
|
+
end
|
63
|
+
|
64
|
+
def full_errors_on_attribute
|
65
|
+
object.errors.full_messages_for(key)
|
66
|
+
end
|
67
|
+
|
68
|
+
def errors_on_association
|
69
|
+
reflection ? object.errors[reflection.name] : []
|
70
|
+
end
|
71
|
+
|
72
|
+
def full_errors_on_association
|
73
|
+
reflection ? object.errors.full_messages_for(reflection.name) : []
|
74
|
+
end
|
75
|
+
|
76
|
+
def has_custom_error?
|
77
|
+
options[:error].is_a?(String)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Determines if the associated object is in a valid state
|
81
|
+
#
|
82
|
+
# An object is considered valid if it is persisted and has no errors.
|
83
|
+
#
|
84
|
+
# @return [Boolean] true if the object is persisted and has no errors, false otherwise
|
85
|
+
def object_valid?
|
86
|
+
object.respond_to?(:persisted?) && object.persisted? &&
|
87
|
+
object.respond_to?(:errors) && !object.errors.empty?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|