phlexi-display 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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