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.
- checksums.yaml +4 -4
- data/lib/phlexi/display/base.rb +35 -158
- data/lib/phlexi/display/components/base.rb +1 -14
- data/lib/phlexi/display/components/concerns/displays_value.rb +54 -0
- data/lib/phlexi/display/components/date_time.rb +49 -0
- data/lib/phlexi/display/components/{error.rb → description.rb} +5 -5
- data/lib/phlexi/display/components/hint.rb +1 -1
- data/lib/phlexi/display/components/label.rb +3 -15
- data/lib/phlexi/display/components/number.rb +37 -0
- data/lib/phlexi/display/components/placeholder.rb +15 -0
- data/lib/phlexi/display/components/string.rb +17 -0
- data/lib/phlexi/display/components/wrapper.rb +4 -18
- data/lib/phlexi/display/field_options/associations.rb +2 -2
- data/lib/phlexi/display/field_options/attachments.rb +21 -0
- data/lib/phlexi/display/field_options/description.rb +22 -0
- data/lib/phlexi/display/field_options/hints.rb +1 -1
- data/lib/phlexi/display/field_options/inferred_types.rb +26 -52
- data/lib/phlexi/display/field_options/{placeholder.rb → placeholders.rb} +2 -2
- data/lib/phlexi/display/field_options/themes.rb +45 -120
- data/lib/phlexi/display/structure/dom.rb +7 -27
- data/lib/phlexi/display/structure/field_builder.rb +75 -151
- data/lib/phlexi/display/structure/field_collection.rb +5 -20
- data/lib/phlexi/display/structure/namespace.rb +22 -34
- data/lib/phlexi/display/structure/namespace_collection.rb +1 -9
- data/lib/phlexi/display/structure/node.rb +9 -3
- data/lib/phlexi/display/version.rb +1 -1
- data/lib/phlexi/display.rb +0 -1
- metadata +11 -31
- data/lib/phlexi/display/components/checkbox.rb +0 -48
- data/lib/phlexi/display/components/collection_checkboxes.rb +0 -44
- data/lib/phlexi/display/components/collection_radio_buttons.rb +0 -35
- data/lib/phlexi/display/components/concerns/handles_array_input.rb +0 -21
- data/lib/phlexi/display/components/concerns/handles_input.rb +0 -53
- data/lib/phlexi/display/components/concerns/has_options.rb +0 -37
- data/lib/phlexi/display/components/concerns/submits_form.rb +0 -39
- data/lib/phlexi/display/components/file_input.rb +0 -32
- data/lib/phlexi/display/components/full_error.rb +0 -21
- data/lib/phlexi/display/components/input.rb +0 -84
- data/lib/phlexi/display/components/input_array.rb +0 -45
- data/lib/phlexi/display/components/radio_button.rb +0 -41
- data/lib/phlexi/display/components/select.rb +0 -69
- data/lib/phlexi/display/components/submit_button.rb +0 -41
- data/lib/phlexi/display/components/textarea.rb +0 -34
- data/lib/phlexi/display/field_options/autofocus.rb +0 -18
- data/lib/phlexi/display/field_options/collection.rb +0 -54
- data/lib/phlexi/display/field_options/disabled.rb +0 -18
- data/lib/phlexi/display/field_options/errors.rb +0 -92
- data/lib/phlexi/display/field_options/length.rb +0 -53
- data/lib/phlexi/display/field_options/limit.rb +0 -66
- data/lib/phlexi/display/field_options/min_max.rb +0 -92
- data/lib/phlexi/display/field_options/multiple.rb +0 -65
- data/lib/phlexi/display/field_options/pattern.rb +0 -38
- data/lib/phlexi/display/field_options/readonly.rb +0 -18
- data/lib/phlexi/display/field_options/required.rb +0 -37
- 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
|
14
|
-
@
|
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
|
-
|
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 :
|
30
|
-
|
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
|
-
:
|
28
|
+
:boolean
|
29
|
+
when :json, :jsonb, :hstore
|
30
|
+
:code
|
52
31
|
else
|
53
|
-
|
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
|
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
|
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 =
|
91
|
+
custom_type = custom_string_display_type(key)
|
103
92
|
return custom_type if custom_type
|
104
93
|
|
105
|
-
|
106
|
-
infer_string_input_type_from_validations
|
107
|
-
else
|
108
|
-
:text
|
109
|
-
end
|
94
|
+
:text
|
110
95
|
end
|
111
96
|
|
112
|
-
def
|
97
|
+
def custom_string_display_type(key)
|
113
98
|
custom_mappings = {
|
114
99
|
/url$|^link|^site/ => :url,
|
115
100
|
/^email/ => :email,
|
116
|
-
|
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
|
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
|
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
|
10
|
-
# It
|
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
|
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(:
|
20
|
-
# # => "
|
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
|
29
|
-
# themed(:
|
30
|
-
# # => "
|
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
|
-
|
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: {
|
54
|
-
# resolve_theme(:
|
55
|
-
# # => "
|
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
|
-
#
|
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
|
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
|
67
|
+
# components.
|
100
68
|
#
|
101
|
-
# @return [Hash] A hash containing theme definitions for
|
69
|
+
# @return [Hash] A hash containing theme definitions for display components
|
102
70
|
#
|
103
71
|
# @example Accessing the theme
|
104
|
-
# theme[:
|
105
|
-
# # => "
|
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[:
|
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
|
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
|
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[:
|
134
|
-
# # =>
|
96
|
+
# default_theme[:text]
|
97
|
+
# # => "text-gray-700 text-sm"
|
135
98
|
#
|
136
99
|
# @example Theme inheritance
|
137
|
-
# default_theme[:
|
138
|
-
# # => :
|
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
|
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
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
#
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
#
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
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
|
7
|
-
# norms that were established by Rails. These can be used
|
8
|
-
# other Ruby web frameworks since it has
|
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
|
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
|
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
|
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}
|
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
|