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
@@ -10,47 +10,32 @@ module Phlexi
10
10
  @inferred_db_type ||= infer_db_type
11
11
  end
12
12
 
13
- def inferred_input_component
14
- @inferred_input_component ||= infer_input_component
15
- end
16
-
17
- def inferred_input_type
18
- @inferred_input_type ||= infer_input_type(inferred_input_component)
13
+ def inferred_display_component
14
+ @inferred_display_component ||= infer_display_component
19
15
  end
20
16
 
21
17
  private
22
18
 
23
- # this returns the element type
24
- # one of :input, :textarea, :select, :botton
25
- def infer_input_component
26
- return :select unless collection.blank?
27
-
19
+ def infer_display_component
28
20
  case inferred_db_type
29
- when :text, :json, :jsonb, :hstore
30
- :textarea
31
- else
32
- :input
33
- end
34
- end
35
-
36
- # this only applies when input_component is `:input`
37
- # resolves the type attribute of input components
38
- def infer_input_type(component)
39
- case inferred_db_type
40
- when :string
41
- infer_string_input_type(key)
21
+ when :string, :text
22
+ infer_string_display_type(key)
42
23
  when :integer, :float, :decimal
43
24
  :number
44
- when :date
25
+ when :date, :datetime, :time
45
26
  :date
46
- when :datetime
47
- :datetime
48
- when :time
49
- :time
50
27
  when :boolean
51
- :checkbox
28
+ :boolean
29
+ when :json, :jsonb, :hstore
30
+ :code
52
31
  else
53
- :text
32
+ if association_reflection
33
+ :association
34
+ elsif attachment_reflection
35
+ :attachment
36
+ else
37
+ :text
38
+ end
54
39
  end
55
40
  end
56
41
 
@@ -81,40 +66,39 @@ module Phlexi
81
66
  case value
82
67
  when Integer
83
68
  :integer
84
- when Float, BigDecimal
69
+ when Float
85
70
  :float
71
+ when BigDecimal
72
+ :decimal
86
73
  when TrueClass, FalseClass
87
74
  :boolean
88
75
  when Date
89
76
  :date
90
77
  when Time, DateTime
91
78
  :datetime
79
+ when Hash
80
+ :json
92
81
  else
93
82
  :string
94
83
  end
95
84
  end
96
85
 
97
- def infer_string_input_type(key)
86
+ def infer_string_display_type(key)
98
87
  key = key.to_s.downcase
99
88
 
100
89
  return :password if is_password_field?
101
90
 
102
- custom_type = custom_string_input_type(key)
91
+ custom_type = custom_string_display_type(key)
103
92
  return custom_type if custom_type
104
93
 
105
- if has_validators?
106
- infer_string_input_type_from_validations
107
- else
108
- :text
109
- end
94
+ :text
110
95
  end
111
96
 
112
- def custom_string_input_type(key)
97
+ def custom_string_display_type(key)
113
98
  custom_mappings = {
114
99
  /url$|^link|^site/ => :url,
115
100
  /^email/ => :email,
116
- /^search/ => :search,
117
- /phone|tel(ephone)?/ => :tel,
101
+ /phone|tel(ephone)?/ => :phone,
118
102
  /^time/ => :time,
119
103
  /^date/ => :date,
120
104
  /^number|_count$|_amount$/ => :number,
@@ -128,16 +112,6 @@ module Phlexi
128
112
  nil
129
113
  end
130
114
 
131
- def infer_string_input_type_from_validations
132
- if attribute_validators.find { |v| v.kind == :numericality }
133
- :number
134
- elsif attribute_validators.find { |v| v.kind == :format && v.options[:with] == URI::MailTo::EMAIL_REGEXP }
135
- :email
136
- else
137
- :text
138
- end
139
- end
140
-
141
115
  def is_password_field?
142
116
  key = self.key.to_s.downcase
143
117
 
@@ -3,10 +3,10 @@
3
3
  module Phlexi
4
4
  module Display
5
5
  module FieldOptions
6
- module Placeholder
6
+ module Placeholders
7
7
  def placeholder(placeholder = nil)
8
8
  if placeholder.nil?
9
- options[:placeholder]
9
+ options[:placeholder] || "-"
10
10
  else
11
11
  options[:placeholder] = placeholder
12
12
  self
@@ -4,41 +4,33 @@ module Phlexi
4
4
  module Display
5
5
  module FieldOptions
6
6
  module Themes
7
- # Resolves theme classes for components based on their type and the validity state of the field.
7
+ # Resolves theme classes for components based on their type.
8
8
  #
9
- # This method is responsible for determining the appropriate CSS classes for a given form component.
10
- # It considers both the base theme for the component type and any additional theming based on the
11
- # component's validity state (valid, invalid, or neutral). The method supports a hierarchical
12
- # theming system, allowing for cascading themes and easy customization.
9
+ # This method is responsible for determining the appropriate CSS classes for a given display component.
10
+ # It supports a hierarchical theming system, allowing for cascading themes and easy customization.
13
11
  #
14
- # @param component [Symbol, String] The type of form component (e.g., :input, :label, :wrapper)
12
+ # @param component [Symbol, String] The type of display component (e.g., :text, :date, :boolean)
15
13
  #
16
14
  # @return [String, nil] A string of CSS classes for the component, or nil if no theme is applied
17
15
  #
18
16
  # @example Basic usage
19
- # themed(:input)
20
- # # => "w-full p-2 border rounded-md shadow-sm font-medium text-sm dark:bg-gray-700"
21
- #
22
- # @example Usage with validity state
23
- # # Assuming the field has errors
24
- # themed(:input)
25
- # # => "w-full p-2 border rounded-md shadow-sm font-medium text-sm dark:bg-gray-700 bg-red-50 border-red-500 text-red-900"
17
+ # themed(:text)
18
+ # # => "text-gray-700 text-sm"
26
19
  #
27
20
  # @example Cascading themes
28
- # # Assuming textarea inherits from input in the theme definition
29
- # themed(:textarea)
30
- # # => "w-full p-2 border rounded-md shadow-sm font-medium text-sm dark:bg-gray-700"
21
+ # # Assuming email inherits from text in the theme definition
22
+ # themed(:email)
23
+ # # => "text-gray-700 text-sm text-blue-600 underline"
31
24
  #
32
25
  # @note The actual CSS classes returned will depend on the theme definitions in the `theme` hash
33
26
  # and any overrides specified in the `options` hash.
34
27
  #
35
28
  # @see #resolve_theme
36
- # @see #resolve_validity_theme
37
29
  # @see #theme
38
30
  def themed(component)
39
31
  return unless component
40
32
 
41
- tokens(resolve_theme(component), resolve_validity_theme(component)).presence
33
+ resolve_theme(component)
42
34
  end
43
35
 
44
36
  protected
@@ -50,9 +42,9 @@ module Phlexi
50
42
  # @return [String, nil] The resolved theme value or nil if not found
51
43
  #
52
44
  # @example Resolving a nested theme
53
- # # Assuming the theme is: { input: :base_input, base_input: "some-class" }
54
- # resolve_theme(:input)
55
- # # => "some-class"
45
+ # # Assuming the theme is: { email: :text, text: "text-gray-700" }
46
+ # resolve_theme(:email)
47
+ # # => "text-gray-700"
56
48
  def resolve_theme(property, visited = Set.new)
57
49
  return nil if !property.present? || visited.include?(property)
58
50
  visited.add(property)
@@ -65,52 +57,23 @@ module Phlexi
65
57
  end
66
58
  end
67
59
 
68
- # Resolves the theme for a component based on its current validity state
69
- #
70
- # This method determines the validity state of the field (valid, invalid, or neutral)
71
- # and returns the corresponding theme by prepending the state to the component name.
72
- #
73
- # @param property [Symbol, String] The base theme property to resolve
74
- # @return [String, nil] The resolved validity-specific theme or nil if not found
75
- #
76
- # @example Resolving a validity theme
77
- # # Assuming the field has errors and the theme includes { invalid_input: "error-class" }
78
- # resolve_validity_theme(:input)
79
- # # => "error-class"
80
- def resolve_validity_theme(property)
81
- validity_property = if has_errors?
82
- :"invalid_#{property}"
83
- elsif object_valid?
84
- :"valid_#{property}"
85
- else
86
- :"neutral_#{property}"
87
- end
88
-
89
- resolve_theme(validity_property)
90
- end
91
-
92
- # Retrieves or initializes the theme hash for the form builder.
60
+ # Retrieves or initializes the theme hash for the display builder.
93
61
  #
94
- # This method returns a hash containing theme definitions for various form components.
62
+ # This method returns a hash containing theme definitions for various display components.
95
63
  # If a theme has been explicitly set in the options, it returns that. Otherwise, it
96
64
  # initializes and returns a default theme.
97
65
  #
98
66
  # The theme hash defines CSS classes or references to other theme keys for different
99
- # components and their states (e.g., valid, invalid, neutral).
67
+ # components.
100
68
  #
101
- # @return [Hash] A hash containing theme definitions for form components
69
+ # @return [Hash] A hash containing theme definitions for display components
102
70
  #
103
71
  # @example Accessing the theme
104
- # theme[:input]
105
- # # => "w-full p-2 border rounded-md shadow-sm font-medium text-sm dark:bg-gray-700"
106
- #
107
- # @example Accessing a validity-specific theme
108
- # theme[:invalid_input]
109
- # # => "bg-red-50 border-red-500 text-red-900 placeholder-red-700 focus:ring-red-500 focus:border-red-500"
72
+ # theme[:text]
73
+ # # => "text-gray-700 text-sm"
110
74
  #
111
75
  # @example Theme inheritance
112
- # theme[:textarea] # Returns :input, indicating textarea inherits input's theme
113
- # theme[:valid_textarea] # Returns :valid_input
76
+ # theme[:email] # Returns :text, indicating email inherits text's theme
114
77
  #
115
78
  # @note The actual content of the theme hash depends on the default_theme method
116
79
  # and any theme overrides specified in the options when initializing the field builder.
@@ -120,85 +83,47 @@ module Phlexi
120
83
  @theme ||= options[:theme] || default_theme
121
84
  end
122
85
 
123
- # Defines and returns the default theme hash for the field builder.
86
+ # Defines and returns the default theme hash for the display builder.
124
87
  #
125
88
  # This method returns a hash containing the base theme definitions for various components.
126
- # It sets up the default styling and relationships between different components and their states.
89
+ # It sets up the default styling and relationships between different components.
127
90
  # The theme uses a combination of explicit CSS classes and symbolic references to other theme keys,
128
91
  # allowing for a flexible and inheritance-based theming system.
129
92
  #
130
93
  # @return [Hash] A frozen hash containing default theme definitions for components
131
94
  #
132
95
  # @example Accessing the default theme
133
- # default_theme[:input]
134
- # # => nil (indicates that :input doesn't have a default and should be defined by the user)
96
+ # default_theme[:text]
97
+ # # => "text-gray-700 text-sm"
135
98
  #
136
99
  # @example Theme inheritance
137
- # default_theme[:textarea]
138
- # # => :input (indicates that :textarea inherits from :input)
139
- #
140
- # @example Validity state theming
141
- # default_theme[:valid_textarea]
142
- # # => :valid_input (indicates that :valid_textarea inherits from :valid_input)
100
+ # default_theme[:email]
101
+ # # => :text (indicates that :email inherits from :text)
143
102
  #
144
103
  # @note This method returns a frozen hash to prevent accidental modifications.
145
- # To customize the theme, users should provide their own theme hash when initializing the field builder.
146
- # @note Most theme values are set to nil or commented out in the default theme to encourage users
147
- # to define their own styles while maintaining the relationships between components and states.
104
+ # To customize the theme, users should provide their own theme hash when initializing the display builder.
148
105
  #
149
106
  # @see #theme
150
107
  def default_theme
151
108
  {
152
- # # input
153
- # input: nil,
154
- # valid_input: nil,
155
- # invalid_input: nil,
156
- # neutral_input: nil,
157
-
158
- # textarea
159
- textarea: :input,
160
- valid_textarea: :valid_input,
161
- invalid_textarea: :invalid_input,
162
- neutral_textarea: :neutral_input,
163
-
164
- # select
165
- select: :input,
166
- valid_select: :valid_input,
167
- invalid_select: :invalid_input,
168
- neutral_select: :neutral_input,
169
-
170
- # file
171
- file: :input,
172
- valid_file: :valid_input,
173
- invalid_file: :invalid_input,
174
- neutral_file: :neutral_input,
175
-
176
- # misc
177
- # label: nil,
178
- # hint: nil,
179
- # error: nil,
180
- full_error: :error,
181
- # wrapper: nil,
182
- # inner_wrapper: nil,
183
- submit_button: :button
184
-
185
- # # label themes
186
- # label: "md:w-1/6 mt-2 block mb-2 text-sm font-medium",
187
- # invalid_label: "text-red-700 dark:text-red-500",
188
- # valid_label: "text-green-700 dark:text-green-500",
189
- # neutral_label: "text-gray-700 dark:text-white",
190
- # # input themes
191
- # input: "w-full p-2 border rounded-md shadow-sm font-medium text-sm dark:bg-gray-700",
192
- # invalid_input: "bg-red-50 border-red-500 dark:border-red-500 text-red-900 dark:text-red-500 placeholder-red-700 dark:placeholder-red-500 focus:ring-red-500 focus:border-red-500",
193
- # valid_input: "bg-green-50 border-green-500 dark:border-green-500 text-green-900 dark:text-green-400 placeholder-green-700 dark:placeholder-green-500 focus:ring-green-500 focus:border-green-500",
194
- # neutral_input: "border-gray-300 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white focus:ring-primary-500 focus:border-primary-500",
195
- # # hint themes
196
- # hint: "mt-2 text-sm text-gray-500 dark:text-gray-200",
197
- # # error themes
198
- # error: "mt-2 text-sm text-red-600 dark:text-red-500",
199
- # # wrapper themes
200
- # wrapper: "flex flex-col md:flex-row items-start space-y-2 md:space-y-0 md:space-x-2 mb-4",
201
- # inner_wrapper: "md:w-5/6 w-full",
109
+ label: "text-base font-bold text-gray-500 dark:text-gray-400 mb-1",
110
+ description: "text-sm text-gray-400 dark:text-gray-500",
111
+ placeholder: "text-xl font-semibold text-gray-500 dark:text-gray-300 mb-1 italic",
112
+ string: "text-xl font-semibold text-gray-900 dark:text-white mb-1",
113
+ # text: :string,
114
+ number: :string,
115
+ datetime: :string,
116
+ # boolean: :string,
117
+ # code: :string,
118
+ # email: :text,
119
+ # url: :text,
120
+ # phone: :text,
121
+ # color: :text,
122
+ # search: :text,
123
+ # password: :string,
124
+ # association: :string,
125
+ attachment: :string,
126
+ wrapper: nil
202
127
  }.freeze
203
128
  end
204
129
  end
@@ -3,21 +3,21 @@
3
3
  module Phlexi
4
4
  module Display
5
5
  module Structure
6
- # Generates DOM IDs, names, etc. for a Field, Namespace, or Node based on
7
- # norms that were established by Rails. These can be used outsidef or Rails in
8
- # other Ruby web frameworks since it has now dependencies on Rails.
6
+ # Generates DOM IDs for a Field, Namespace, or Node based on
7
+ # norms that were established by Rails. These can be used outside of Rails in
8
+ # other Ruby web frameworks since it has no dependencies on Rails.
9
9
  class DOM
10
10
  def initialize(field:)
11
11
  @field = field
12
12
  end
13
13
 
14
14
  # Converts the value of the field to a String, which is required to work
15
- # with Phlex. Assumes that `Object#to_s` emits a format suitable for the web form.
15
+ # with Phlex. Assumes that `Object#to_s` emits a format suitable for display.
16
16
  def value
17
17
  @field.value.to_s
18
18
  end
19
19
 
20
- # Walks from the current node to the parent node, grabs the names, and seperates
20
+ # Walks from the current node to the parent node, grabs the names, and separates
21
21
  # them with a `_` for a DOM ID.
22
22
  def id
23
23
  @id ||= begin
@@ -27,34 +27,14 @@ module Phlexi
27
27
  end
28
28
  end
29
29
 
30
- # The `name` attribute of a node, which is influenced by Rails.
31
- # All node names, except the parent node, are wrapped in a `[]` and collections
32
- # are left empty. For example, `user[addresses][][street]` would be created for a form with
33
- # data shaped like `{user: {addresses: [{street: "Sesame Street"}]}}`.
34
- def name
35
- @name ||= begin
36
- root, *names = keys
37
- names.map { |name| "[#{name}]" }.unshift(root).join
38
- end
39
- end
40
-
41
30
  # One-liner way of walking from the current node all the way up to the parent.
42
31
  def lineage
43
32
  @lineage ||= Enumerator.produce(@field, &:parent).take_while(&:itself).reverse
44
33
  end
45
34
 
46
- # Emit the id, name, and value in an HTML tag-ish that doesnt have an element.
35
+ # Emit the id and value in an HTML tag-ish that doesn't have an element.
47
36
  def inspect
48
- "<#{self.class.name} id=#{id.inspect} name=#{name.inspect} value=#{value.inspect}/>"
49
- end
50
-
51
- private
52
-
53
- def keys
54
- @keys ||= lineage.map do |node|
55
- # If the parent of a field is a field, the name should be nil.
56
- node.key unless node.parent.is_a? FieldBuilder
57
- end
37
+ "<#{self.class.name} id=#{id.inspect} value=#{value.inspect}/>"
58
38
  end
59
39
  end
60
40
  end