phlexi-display 0.0.1 → 0.0.2

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/lib/phlexi/display/base.rb +35 -158
  3. data/lib/phlexi/display/components/base.rb +1 -14
  4. data/lib/phlexi/display/components/concerns/displays_value.rb +54 -0
  5. data/lib/phlexi/display/components/date_time.rb +49 -0
  6. data/lib/phlexi/display/components/{error.rb → description.rb} +5 -5
  7. data/lib/phlexi/display/components/hint.rb +1 -1
  8. data/lib/phlexi/display/components/label.rb +3 -15
  9. data/lib/phlexi/display/components/number.rb +37 -0
  10. data/lib/phlexi/display/components/placeholder.rb +15 -0
  11. data/lib/phlexi/display/components/string.rb +17 -0
  12. data/lib/phlexi/display/components/wrapper.rb +4 -18
  13. data/lib/phlexi/display/field_options/associations.rb +2 -2
  14. data/lib/phlexi/display/field_options/attachments.rb +21 -0
  15. data/lib/phlexi/display/field_options/description.rb +22 -0
  16. data/lib/phlexi/display/field_options/hints.rb +1 -1
  17. data/lib/phlexi/display/field_options/inferred_types.rb +26 -52
  18. data/lib/phlexi/display/field_options/{placeholder.rb → placeholders.rb} +2 -2
  19. data/lib/phlexi/display/field_options/themes.rb +45 -120
  20. data/lib/phlexi/display/structure/dom.rb +7 -27
  21. data/lib/phlexi/display/structure/field_builder.rb +75 -151
  22. data/lib/phlexi/display/structure/field_collection.rb +5 -20
  23. data/lib/phlexi/display/structure/namespace.rb +22 -34
  24. data/lib/phlexi/display/structure/namespace_collection.rb +1 -9
  25. data/lib/phlexi/display/structure/node.rb +9 -3
  26. data/lib/phlexi/display/version.rb +1 -1
  27. data/lib/phlexi/display.rb +0 -1
  28. metadata +11 -31
  29. data/lib/phlexi/display/components/checkbox.rb +0 -48
  30. data/lib/phlexi/display/components/collection_checkboxes.rb +0 -44
  31. data/lib/phlexi/display/components/collection_radio_buttons.rb +0 -35
  32. data/lib/phlexi/display/components/concerns/handles_array_input.rb +0 -21
  33. data/lib/phlexi/display/components/concerns/handles_input.rb +0 -53
  34. data/lib/phlexi/display/components/concerns/has_options.rb +0 -37
  35. data/lib/phlexi/display/components/concerns/submits_form.rb +0 -39
  36. data/lib/phlexi/display/components/file_input.rb +0 -32
  37. data/lib/phlexi/display/components/full_error.rb +0 -21
  38. data/lib/phlexi/display/components/input.rb +0 -84
  39. data/lib/phlexi/display/components/input_array.rb +0 -45
  40. data/lib/phlexi/display/components/radio_button.rb +0 -41
  41. data/lib/phlexi/display/components/select.rb +0 -69
  42. data/lib/phlexi/display/components/submit_button.rb +0 -41
  43. data/lib/phlexi/display/components/textarea.rb +0 -34
  44. data/lib/phlexi/display/field_options/autofocus.rb +0 -18
  45. data/lib/phlexi/display/field_options/collection.rb +0 -54
  46. data/lib/phlexi/display/field_options/disabled.rb +0 -18
  47. data/lib/phlexi/display/field_options/errors.rb +0 -92
  48. data/lib/phlexi/display/field_options/length.rb +0 -53
  49. data/lib/phlexi/display/field_options/limit.rb +0 -66
  50. data/lib/phlexi/display/field_options/min_max.rb +0 -92
  51. data/lib/phlexi/display/field_options/multiple.rb +0 -65
  52. data/lib/phlexi/display/field_options/pattern.rb +0 -38
  53. data/lib/phlexi/display/field_options/readonly.rb +0 -18
  54. data/lib/phlexi/display/field_options/required.rb +0 -37
  55. data/lib/phlexi/display/field_options/validators.rb +0 -48
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlexi
4
- module Display
5
- module Components
6
- class CollectionRadioButtons < Base
7
- include Concerns::HandlesInput
8
- include Concerns::HasOptions
9
-
10
- def view_template
11
- div(**attributes.slice(:id, :class)) do
12
- field.multi(option_mapper.values) do |builder|
13
- render builder.hidden_field_tag if builder.index == 0
14
-
15
- field = builder.field(
16
- label: option_mapper[builder.key],
17
- # We set the attributes here so they are applied to all input components even if the user decides to use a block
18
- input_attributes: {
19
- checked_value: builder.key,
20
- checked: selected?(builder.key)
21
- }
22
- )
23
- if block_given?
24
- yield field
25
- else
26
- render field.radio_button_tag
27
- render field.label_tag
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlexi
4
- module Display
5
- module Components
6
- module Concerns
7
- module HandlesArrayInput
8
- protected
9
-
10
- def normalize_input(input_value)
11
- normalize_array_input(input_value)
12
- end
13
-
14
- def normalize_array_input(input_value)
15
- Array(input_value).map { |nested_input_value| normalize_simple_input(nested_input_value) }.compact
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlexi
4
- module Display
5
- module Components
6
- module Concerns
7
- module HandlesInput
8
- # Collects parameters matching the input field's param key.
9
- #
10
- # @param params [Hash] the parameters to collect from.
11
- # @return [Hash] the collected parameters.
12
- def extract_input(params)
13
- # # Handles multi parameter attributes
14
- # # https://www.cookieshq.co.uk/posts/rails-spelunking-date-select
15
- # # https://www.cookieshq.co.uk/posts/multiparameter-attributes
16
-
17
- # # Matches
18
- # # - parameter
19
- # # - parameter(1)
20
- # # - parameter(2)
21
- # # - parameter(1i)
22
- # # - parameter(2f)
23
- # regex = /^#{param}(\(\d+[if]?\))?$/
24
- # keys = params.select { |key, _| regex.match?(key) }.keys
25
- # params.slice(*keys)
26
-
27
- params ||= {}
28
- {input_param => normalize_input(params[field.key])}
29
- end
30
-
31
- protected
32
-
33
- def build_attributes
34
- super
35
- @input_param = attributes.delete(:input_param) || field.key
36
- end
37
-
38
- def input_param
39
- @input_param
40
- end
41
-
42
- def normalize_input(input_value)
43
- normalize_simple_input(input_value)
44
- end
45
-
46
- def normalize_simple_input(input_value)
47
- input_value.to_s.presence
48
- end
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlexi
4
- module Display
5
- module Components
6
- module Concerns
7
- module HasOptions
8
- protected
9
-
10
- def build_attributes
11
- super
12
- @collection = attributes.delete(:collection) || field.collection
13
- @label_method = attributes.delete(:label_method)
14
- @value_method = attributes.delete(:value_method)
15
- end
16
-
17
- def option_mapper
18
- @option_mapper ||= OptionMapper.new(@collection, label_method: @label_method, value_method: @value_method)
19
- end
20
-
21
- def selected?(option)
22
- if attributes[:multiple]
23
- @options_list ||= Array(field.value)
24
- @options_list.any? { |item| item.to_s == option.to_s }
25
- else
26
- field.value.to_s == option.to_s
27
- end
28
- end
29
-
30
- def normalize_simple_input(input_value)
31
- ([super] & option_mapper.values)[0]
32
- end
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlexi
4
- module Display
5
- module Components
6
- module Concerns
7
- module SubmitsDisplay
8
- def submit_type_value
9
- if field.object.respond_to?(:persisted?)
10
- field.object.persisted? ? :update : :create
11
- else
12
- :submit
13
- end
14
- end
15
-
16
- def submit_type_label
17
- @submit_type_label ||= begin
18
- key = submit_type_value
19
-
20
- model_object = field.dom.lineage.first.key.to_s
21
- model_name_human = if field.object.respond_to?(:model_name)
22
- field.object.model_name.human
23
- else
24
- model_object.humanize
25
- end
26
-
27
- defaults = []
28
- defaults << :"helpers.submit.#{model_object}.#{key}"
29
- defaults << :"helpers.submit.#{key}"
30
- defaults << "#{key.to_s.humanize} #{model_name_human}"
31
-
32
- I18n.t(defaults.shift, model: model_name_human, default: defaults)
33
- end
34
- end
35
- end
36
- end
37
- end
38
- end
39
- end
@@ -1,32 +0,0 @@
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
@@ -1,21 +0,0 @@
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
@@ -1,84 +0,0 @@
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
@@ -1,45 +0,0 @@
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
@@ -1,41 +0,0 @@
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
@@ -1,69 +0,0 @@
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
@@ -1,41 +0,0 @@
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
@@ -1,34 +0,0 @@
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
@@ -1,18 +0,0 @@
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
@@ -1,54 +0,0 @@
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
@@ -1,18 +0,0 @@
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