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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b140e54c49ca3a4a8808a12bb7a91dd8d250c91e2d7d29a8d7f9be2c20a7dbf5
4
- data.tar.gz: 9c70cec19581873bb9b93fc887571d6c8d5d65c95a25576bdf26a67e2bb30046
3
+ metadata.gz: 6269ae20b09b902ba613e2f158eae3de8c5b6a900264225a0580b97fb77e6ebe
4
+ data.tar.gz: 776a1c152a6e8e877f27747db06575c327ab042923b5068749631351938bae8a
5
5
  SHA512:
6
- metadata.gz: 466d34728adaedd7c4669a7419ec53929e49e1b4466135e5a7cc1eb42c88aa196b344ca4e29d8f27fb56a1e83437500678a2b003ca95fc82bbf70ab1ad017afc
7
- data.tar.gz: 047a28cd2c6afba47d9b6c8a833437ac2c009e626f747663dc9f506ef8d805b7979b8f59e4b0fe6e59a8180538d7e66cf323aba83e26e671e567ff12a3236a2c
6
+ metadata.gz: 6e0b53271d6d515923b6c05f0fd5d83a8f54e413058b0cd023a662e3a1e1d02c62f0205ccf411cd64daf22b1ae2457676aa9d52399444421a2cabe211509a352
7
+ data.tar.gz: 11bb8cd334e94ed31bcd096684cb1e7c844a13704df363dab0a540b0268440f0dcbf5a87973b7cd7ed7016e3bb45c8fe0ddbf0450a61332fd534bd0ae1f04e8d
@@ -1,22 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/module/delegation"
4
- require "active_support/string_inquirer"
5
- require "active_support/core_ext/hash/deep_merge"
6
4
  require "active_support/core_ext/string/inflections"
7
5
 
8
6
  module Phlexi
9
7
  module Display
10
- # A form component for building flexible and customizable forms.
8
+ # A display component for rendering flexible and customizable data views.
11
9
  #
12
10
  # @example Basic usage
13
- # Phlexi::Display.new(user, action: '/users', method: 'post') do |f|
14
- # render field(:name).placeholder("Name").input_tag
15
- # render field(:email).placeholder("Email").input_tag
11
+ # Phlexi::Display.new(user) do |d|
12
+ # render d.field(:name).text
13
+ # render d.field(:email).text
16
14
  # end
17
15
  #
18
- # @attr_reader [Symbol] key The form's key, derived from the record or explicitly set
19
- # @attr_reader [ActiveModel::Model, nil] object The form's associated object
16
+ # @attr_reader [Symbol] key The display's key, derived from the record or explicitly set
17
+ # @attr_reader [ActiveModel::Model, nil] object The display's associated object
20
18
  class Base < COMPONENT_BASE
21
19
  class Namespace < Structure::Namespace; end
22
20
 
@@ -24,22 +22,18 @@ module Phlexi
24
22
 
25
23
  attr_reader :key, :object
26
24
 
27
- delegate :field, :submit_button, :nest_one, :nest_many, to: :@namespace
25
+ delegate :field, :nest_one, :nest_many, to: :@namespace
28
26
 
29
27
  # Initializes a new Display instance.
30
28
  #
31
- # @param record [ActiveModel::Model, Symbol, String] The form's associated record or key
32
- # @param action [String, nil] The form's action URL
33
- # @param method [String, nil] The form's HTTP method
34
- # @param attributes [Hash] Additional HTML attributes for the form tag
35
- # @param options [Hash] Additional options for form configuration
36
- # @option options [String] :class CSS classes for the form
29
+ # @param record [ActiveModel::Model, Symbol, String] The display's associated record or key
30
+ # @param attributes [Hash] Additional HTML attributes for the display container
31
+ # @param options [Hash] Additional options for display configuration
32
+ # @option options [String] :class CSS classes for the display
37
33
  # @option options [Class] :namespace_klass Custom namespace class
38
34
  # @option options [Class] :builder_klass Custom field builder class
39
- def initialize(record, action: nil, method: nil, attributes: {}, **options)
40
- @form_action = action
41
- @form_method = method
42
- @form_class = options.delete(:class)
35
+ def initialize(record, attributes: {}, **options)
36
+ @display_class = options.delete(:class)
43
37
  @attributes = attributes
44
38
  @namespace_klass = options.delete(:namespace_klass) || default_namespace_klass
45
39
  @builder_klass = options.delete(:builder_klass) || default_builder_klass
@@ -47,52 +41,32 @@ module Phlexi
47
41
 
48
42
  initialize_object_and_key(record)
49
43
  initialize_namespace
50
- initialize_attributes
51
44
  end
52
45
 
53
- # Renders the form template.
46
+ # Renders the display template.
54
47
  #
55
48
  # @return [void]
56
49
  def view_template
57
- form_tag { form_template }
50
+ display_template
58
51
  end
59
52
 
60
- # Executes the form's content block.
61
- # Override this in subclasses to defie a static form.
62
- #
63
- # @return [void]
64
- def form_template
65
- instance_exec(&@_content_block) if @_content_block
66
- end
53
+ protected
54
+
55
+ attr_reader :options, :attributes, :namespace_klass, :builder_klass
67
56
 
68
- # Renders the form tag with its contents.
57
+ # Executes the display's content block.
58
+ # Override this in subclasses to define a static display.
69
59
  #
70
- # @yield The form's content
71
60
  # @return [void]
72
- def form_tag(&)
73
- form(**form_attributes) do
74
- render_hidden_method_field
75
- render_authenticity_token if authenticity_token?
76
- yield
77
- end
78
- end
79
-
80
- def extract_input(params)
81
- call unless @_rendered
82
- @namespace.extract_input(params)
61
+ def display_template
62
+ instance_exec(&@_content_block) if @_content_block
83
63
  end
84
64
 
85
- protected
86
-
87
- attr_reader :options, :attributes, :namespace_klass, :builder_klass
88
-
89
65
  # Initializes the object and key based on the given record.
90
66
  #
91
- # @param record [ActiveModel::Model, Symbol, String] The form's associated record or key
67
+ # @param record [ActiveModel::Model, Symbol, String] The display's associated record or key
92
68
  # @return [void]
93
69
  def initialize_object_and_key(record)
94
- # always pop these keys
95
- # add support for `as` to make it more rails friendly
96
70
  @key = options.delete(:key) || options.delete(:as)
97
71
 
98
72
  case record
@@ -101,134 +75,37 @@ module Phlexi
101
75
  @key = record
102
76
  else
103
77
  @object = record
104
- if @key.nil?
105
- unless object.respond_to?(:model_name) && object.model_name.respond_to?(:param_key) && object.model_name.param_key.present?
106
- raise ArgumentError, "record must respond to #model_name.param_key with a non nil value or set `key` option e.g. Phlexi::Display(record, key: :record)"
107
- end
108
- @key = object.model_name.param_key
78
+ @key = if @key.nil? && object.respond_to?(:model_name) && object.model_name.respond_to?(:param_key) && object.model_name.param_key.present?
79
+ object.model_name.param_key
80
+ else
81
+ :object
109
82
  end
110
83
  end
111
84
  @key = @key.to_sym
112
85
  end
113
86
 
114
- # Initializes the namespace for the form.
87
+ # Initializes the namespace for the display.
115
88
  #
116
89
  # @return [void]
117
90
  def initialize_namespace
118
91
  @namespace = namespace_klass.root(key, object: object, builder_klass: builder_klass)
119
92
  end
120
-
121
- # Initializes form attributes.
122
- #
123
- # @return [void]
124
- def initialize_attributes
125
- attributes.fetch(:accept_charset) { attributes[:accept_charset] = "UTF-8" }
126
- end
127
-
128
- # Determines the form's action URL.
129
- #
130
- # @return [String, nil] The form's action URL
131
- def form_action
132
- puts ""
133
- # if @form_action != false
134
- # @form_action ||= if options[:format].nil?
135
- # polymorphic_path(object, {})
136
- # else
137
- # polymorphic_path(object, format: options[:format])
138
- # end
139
- # end
140
- @form_action
141
- end
142
-
143
- # Determines the form's HTTP method.
144
- #
145
- # @return [ActiveSupport::StringInquirer] The form's HTTP method
146
- def form_method
147
- @form_method ||= (object_form_method || "get").to_s.downcase
148
- ActiveSupport::StringInquirer.new(@form_method)
149
- end
150
-
151
- # Retrieves the form's CSS classes.
152
- #
153
- # @return [String] The form's CSS classes
154
- attr_reader :form_class
155
-
156
- # Checks if the authenticity token should be included.
157
- #
158
- # @return [Boolean] True if the authenticity token should be included, false otherwise
159
- def authenticity_token?
160
- defined?(helpers) && options.fetch(:authenticity_token) { !form_method.get? }
161
- end
162
-
163
- # Retrieves the authenticity token.
164
- #
165
- # @return [String] The authenticity token
166
- def authenticity_token
167
- options.fetch(:authenticity_token) { helpers.form_authenticity_token }
168
- end
169
-
170
- # Renders the authenticity token field.
171
- #
172
- # @param name [String] The name attribute for the authenticity token field
173
- # @param value [String] The value for the authenticity token field
174
- # @return [void]
175
- def authenticity_token_field(name = "authenticity_token", value = authenticity_token)
176
- input(name: name, value: value, type: "hidden", hidden: true)
177
- end
178
-
179
- # Determines the appropriate form method based on the object's state.
93
+ # Retrieves the display's CSS classes.
180
94
  #
181
- # @return [String, nil] The appropriate form method
182
- def object_form_method
183
- if object.respond_to?(:persisted?)
184
- object.persisted? ? "patch" : "post"
185
- elsif object.present?
186
- "post"
187
- end
188
- end
95
+ # @return [String] The display's CSS classes
96
+ attr_reader :display_class
189
97
 
190
- # Renders the hidden method field for non-standard HTTP methods.
98
+ # Generates the display attributes hash.
191
99
  #
192
- # @return [void]
193
- def render_hidden_method_field
194
- return if standard_form_method?
195
- input(name: "_method", value: form_method, type: "hidden", hidden: true)
196
- end
197
-
198
- # Checks if the form method is standard (GET or POST).
199
- #
200
- # @return [Boolean] True if the form method is standard, false otherwise
201
- def standard_form_method?
202
- form_method.get? || form_method.post?
203
- end
204
-
205
- # Returns the standardized form method for the HTML form tag.
206
- #
207
- # @return [String] The standardized form method
208
- def standardized_form_method
209
- standard_form_method? ? form_method : "post"
210
- end
211
-
212
- # Generates the form attributes hash.
213
- #
214
- # @return [Hash] The form attributes
215
- def form_attributes
100
+ # @return [Hash] The display attributes
101
+ def display_attributes
216
102
  {
217
103
  id: @namespace.dom_id,
218
- action: form_action,
219
- method: standardized_form_method,
220
- class: form_class,
104
+ class: display_class,
221
105
  **attributes
222
106
  }
223
107
  end
224
108
 
225
- # Renders the authenticity token if required.
226
- #
227
- # @return [void]
228
- def render_authenticity_token
229
- authenticity_token_field
230
- end
231
-
232
109
  private
233
110
 
234
111
  def default_namespace_klass
@@ -25,12 +25,7 @@ module Phlexi
25
25
 
26
26
  attributes[:class] = tokens(
27
27
  component_name,
28
- attributes[:class],
29
- -> { attributes[:required] } => "required",
30
- -> { !attributes[:required] } => "optional",
31
- -> { field.has_errors? } => "invalid",
32
- -> { attributes[:readonly] } => "readonly",
33
- -> { attributes[:disabled] } => "disabled"
28
+ attributes[:class]
34
29
  )
35
30
  end
36
31
 
@@ -41,11 +36,3 @@ module Phlexi
41
36
  end
42
37
  end
43
38
  end
44
-
45
-
46
-
47
- User
48
- name: :string
49
- validate :name, presence: true
50
-
51
- f.input :name
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Display
5
+ module Components
6
+ module Concerns
7
+ module DisplaysValue
8
+ def view_template
9
+ value = normalize_value(field.value)
10
+ if value.blank?
11
+ render field.placeholder_tag(**@placeholder_attributes)
12
+ else
13
+ render_value(value)
14
+ end
15
+ end
16
+
17
+ # Renders the field value for display.
18
+ #
19
+ # @return [String] the formatted field value for display.
20
+ def render_value(value)
21
+ raise NotImplementedError, "#{self.class}#render_value"
22
+ # format_value()
23
+ end
24
+
25
+ protected
26
+
27
+ def build_attributes
28
+ super
29
+
30
+ @placeholder_attributes = attributes.delete(:placeholder_attributes) || {}
31
+ attributes[:id] = field.dom.id if attributes[:id] == "#{field.dom.id}_#{component_name}"
32
+ end
33
+
34
+ # def format_value(value)
35
+ # case value
36
+ # when Array
37
+ # format_array_value(value)
38
+ # else
39
+ # format_single_value(value)
40
+ # end
41
+ # end
42
+
43
+ # def format_array_value(array)
44
+ # array.map { |item| format_single_value(item) }.join(", ")
45
+ # end
46
+
47
+ def normalize_value(value)
48
+ value.to_s
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Display
5
+ module Components
6
+ class DateTime < Base
7
+ include Concerns::DisplaysValue
8
+
9
+ # Renders the date time value
10
+ #
11
+ # @param value [String, Date, Time, DateTime] The value to be rendered
12
+ # @return [void]
13
+ def render_value(value)
14
+ formatted_value = format_date_time(value)
15
+ p(**attributes) { formatted_value }
16
+ end
17
+
18
+ protected
19
+
20
+ def build_attributes
21
+ super
22
+
23
+ @options = {
24
+ format: default_format
25
+ }.merge(attributes.delete(:options) || {}).compact
26
+ end
27
+
28
+ private
29
+
30
+ def format_date_time(value)
31
+ I18n.l(value, **@options)
32
+ end
33
+
34
+ def default_format
35
+ :long
36
+ end
37
+
38
+ def normalize_value(value)
39
+ case value
40
+ when Date, DateTime, Time, nil
41
+ value
42
+ else
43
+ raise ArgumentError, "Value must be a Date, DateTime or Time object. #{value.inspect} given."
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -3,17 +3,17 @@
3
3
  module Phlexi
4
4
  module Display
5
5
  module Components
6
- class Error < Base
6
+ class Description < Base
7
7
  def view_template
8
- p(**attributes) do
9
- field.error
10
- end
8
+ p(**attributes) {
9
+ field.description
10
+ }
11
11
  end
12
12
 
13
13
  private
14
14
 
15
15
  def render?
16
- field.show_errors? && field.has_errors?
16
+ field.has_description?
17
17
  end
18
18
  end
19
19
  end
@@ -13,7 +13,7 @@ module Phlexi
13
13
  private
14
14
 
15
15
  def render?
16
- field.hint.present? && (!field.show_errors? || !field.has_errors?)
16
+ field.has_hint?
17
17
  end
18
18
  end
19
19
  end
@@ -5,21 +5,9 @@ module Phlexi
5
5
  module Components
6
6
  class Label < Base
7
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 }
8
+ h5(**attributes) {
9
+ field.label
10
+ }
23
11
  end
24
12
  end
25
13
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/number_helper"
4
+
5
+ module Phlexi
6
+ module Display
7
+ module Components
8
+ class Number < Base
9
+ include Concerns::DisplaysValue
10
+
11
+ def render_value(value)
12
+ p(**attributes) {
13
+ format_number(value)
14
+ }
15
+ end
16
+
17
+ protected
18
+
19
+ def build_attributes
20
+ super
21
+
22
+ @options = attributes.delete(:options) || {}
23
+ end
24
+
25
+ private
26
+
27
+ def format_number(value)
28
+ ActiveSupport::NumberHelper.number_to_delimited(value, **@options)
29
+ end
30
+
31
+ def normalize_value(value)
32
+ Float(value.to_s)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Display
5
+ module Components
6
+ class Placeholder < Base
7
+ def view_template
8
+ p(**attributes) {
9
+ field.placeholder
10
+ }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Display
5
+ module Components
6
+ class String < Base
7
+ include Concerns::DisplaysValue
8
+
9
+ def render_value(value)
10
+ p(**attributes) {
11
+ value
12
+ }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -4,26 +4,12 @@ module Phlexi
4
4
  module Display
5
5
  module Components
6
6
  class Wrapper < Base
7
- attr_reader :inner_attributes
8
-
9
7
  def view_template
10
- div(**attributes) do
8
+ div(**attributes) {
11
9
  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])
10
+ yield field if block_given?
11
+ render field.description_tag
12
+ }
27
13
  end
28
14
  end
29
15
  end
@@ -6,8 +6,8 @@ module Phlexi
6
6
  module Associations
7
7
  protected
8
8
 
9
- def reflection
10
- @reflection ||= find_association_reflection
9
+ def association_reflection
10
+ @association_reflection ||= find_association_reflection
11
11
  end
12
12
 
13
13
  def find_association_reflection
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Display
5
+ module FieldOptions
6
+ module Attachments
7
+ protected
8
+
9
+ def attachment_reflection
10
+ @attachment_reflection ||= find_attachment_reflection
11
+ end
12
+
13
+ def find_attachment_reflection
14
+ if object.class.respond_to?(:reflect_on_attachment)
15
+ object.class.reflect_on_attachment(key)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Display
5
+ module FieldOptions
6
+ module Description
7
+ def description(description = nil)
8
+ if description.nil?
9
+ options[:description]
10
+ else
11
+ options[:description] = description
12
+ self
13
+ end
14
+ end
15
+
16
+ def has_description?
17
+ description.present?
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -14,7 +14,7 @@ module Phlexi
14
14
  end
15
15
 
16
16
  def has_hint?
17
- options[:hint] != false && hint.present?
17
+ hint.present?
18
18
  end
19
19
  end
20
20
  end