phlexi-display 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/phlexi/display/base.rb +37 -30
  3. data/lib/phlexi/display/builder.rb +174 -0
  4. data/lib/phlexi/display/components/association.rb +41 -0
  5. data/lib/phlexi/display/components/base.rb +2 -29
  6. data/lib/phlexi/display/components/color.rb +20 -0
  7. data/lib/phlexi/display/components/concerns/displays_value.rb +5 -19
  8. data/lib/phlexi/display/components/date_time.rb +14 -14
  9. data/lib/phlexi/display/components/email.rb +43 -0
  10. data/lib/phlexi/display/components/enum.rb +17 -0
  11. data/lib/phlexi/display/components/integer.rb +17 -0
  12. data/lib/phlexi/display/components/json.rb +25 -0
  13. data/lib/phlexi/display/components/password.rb +23 -0
  14. data/lib/phlexi/display/components/placeholder.rb +1 -1
  15. data/lib/phlexi/display/components/time.rb +15 -0
  16. data/lib/phlexi/display/components/url.rb +44 -0
  17. data/lib/phlexi/display/html.rb +15 -0
  18. data/lib/phlexi/display/options/inferred_types.rb +29 -0
  19. data/lib/phlexi/display/theme.rb +35 -0
  20. data/lib/phlexi/display/version.rb +1 -1
  21. data/lib/phlexi/display.rb +4 -6
  22. metadata +29 -17
  23. data/lib/phlexi/display/field_options/associations.rb +0 -21
  24. data/lib/phlexi/display/field_options/attachments.rb +0 -21
  25. data/lib/phlexi/display/field_options/description.rb +0 -22
  26. data/lib/phlexi/display/field_options/hints.rb +0 -22
  27. data/lib/phlexi/display/field_options/inferred_types.rb +0 -129
  28. data/lib/phlexi/display/field_options/labels.rb +0 -28
  29. data/lib/phlexi/display/field_options/placeholders.rb +0 -18
  30. data/lib/phlexi/display/field_options/themes.rb +0 -132
  31. data/lib/phlexi/display/option_mapper.rb +0 -154
  32. data/lib/phlexi/display/structure/dom.rb +0 -42
  33. data/lib/phlexi/display/structure/field_builder.rb +0 -160
  34. data/lib/phlexi/display/structure/field_collection.rb +0 -39
  35. data/lib/phlexi/display/structure/namespace.rb +0 -123
  36. data/lib/phlexi/display/structure/namespace_collection.rb +0 -40
  37. data/lib/phlexi/display/structure/node.rb +0 -24
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Display
5
+ module Components
6
+ class Url < Base
7
+ include Concerns::DisplaysValue
8
+
9
+ def render_value(value)
10
+ a(**attributes, href: value, target: "_blank") {
11
+ icon
12
+ plain value
13
+ }
14
+ end
15
+
16
+ protected
17
+
18
+ def icon
19
+ icon_theme = themed(:prefixed_icon)
20
+ svg(
21
+ xmlns: "http://www.w3.org/2000/svg",
22
+ width: icon_theme || "24",
23
+ height: icon_theme || "24",
24
+ class: icon_theme,
25
+ viewbox: "0 0 24 24",
26
+ fill: "none",
27
+ stroke: "currentColor",
28
+ stroke_width: "2",
29
+ stroke_linecap: "round",
30
+ stroke_linejoin: "round"
31
+ ) do |s|
32
+ s.path(stroke: "none", d: "M0 0h24v24H0z", fill: "none")
33
+ s.path(d: "M9 15l6 -6")
34
+ s.path(d: "M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464")
35
+ s.path(
36
+ d:
37
+ "M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463"
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ module Phlexi
2
+ module Display
3
+ class HTML < (defined?(::ApplicationComponent) ? ::ApplicationComponent : Phlex::HTML)
4
+ module Behaviour
5
+ protected
6
+
7
+ def themed(component)
8
+ Phlexi::Display::Theme.instance.resolve_theme(component)
9
+ end
10
+ end
11
+
12
+ include Behaviour
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Display
5
+ module Options
6
+ module InferredTypes
7
+ private
8
+
9
+ def infer_field_component
10
+ case inferred_field_type
11
+ when :string, :text
12
+ infer_string_field_type || inferred_field_type
13
+ when :float, :decimal
14
+ :number
15
+ when :json, :jsonb
16
+ :json
17
+ # when :attachment, :binary
18
+ # :attachment
19
+ # end
20
+ when :integer, :association, :hstore, :date, :time, :datetime
21
+ inferred_field_type
22
+ else
23
+ :string
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ module Phlexi
2
+ module Display
3
+ class Theme < Phlexi::Field::Theme
4
+ def self.theme
5
+ @theme ||= {
6
+ base: nil,
7
+ label: nil,
8
+ description: nil,
9
+ placeholder: nil,
10
+ string: nil,
11
+ text: :string,
12
+ phone: :string,
13
+ number: :string,
14
+ integer: :string,
15
+ datetime: :string,
16
+ date: :datetime,
17
+ time: :datetime,
18
+ association: :string,
19
+ attachment: :string,
20
+ color: :string,
21
+ color_icon: :string,
22
+ email: :string,
23
+ url: :email,
24
+ json: :string,
25
+ hstore: :json,
26
+ password: :string,
27
+ enum: :string,
28
+ prefixed_icon: nil,
29
+ link: nil,
30
+ wrapper: nil
31
+ }.freeze
32
+ end
33
+ end
34
+ end
35
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Phlexi
4
4
  module Display
5
- VERSION = "0.0.2"
5
+ VERSION = "0.0.4"
6
6
  end
7
7
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "zeitwerk"
4
4
  require "phlex"
5
+ require "phlexi-field"
5
6
  require "active_support/core_ext/object/blank"
6
7
 
7
8
  module Phlexi
@@ -11,20 +12,17 @@ module Phlexi
11
12
  loader.inflector.inflect(
12
13
  "phlexi-display" => "Phlexi",
13
14
  "phlexi" => "Phlexi",
14
- "dom" => "DOM"
15
+ "html" => "HTML",
16
+ "json" => "JSON"
15
17
  )
16
18
  loader.push_dir(File.expand_path("..", __dir__))
17
19
  loader.setup
18
20
  end
19
21
 
20
- COMPONENT_BASE = (defined?(::ApplicationComponent) ? ::ApplicationComponent : Phlex::HTML)
21
-
22
- NIL_VALUE = :__i_phlexi_display_nil_value_i__
23
-
24
22
  class Error < StandardError; end
25
23
  end
26
24
  end
27
25
 
28
26
  def Phlexi.Display(...)
29
- Phlexi::Display::Base.new(...)
27
+ Phlexi::Display::Base.inline(...)
30
28
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlexi-display
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Froelich
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-26 00:00:00.000000000 Z
11
+ date: 2024-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: phlex
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: phlexi-field
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: activesupport
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -185,31 +199,29 @@ files:
185
199
  - lib/phlexi-display.rb
186
200
  - lib/phlexi/display.rb
187
201
  - lib/phlexi/display/base.rb
202
+ - lib/phlexi/display/builder.rb
203
+ - lib/phlexi/display/components/association.rb
188
204
  - lib/phlexi/display/components/base.rb
205
+ - lib/phlexi/display/components/color.rb
189
206
  - lib/phlexi/display/components/concerns/displays_value.rb
190
207
  - lib/phlexi/display/components/date_time.rb
191
208
  - lib/phlexi/display/components/description.rb
209
+ - lib/phlexi/display/components/email.rb
210
+ - lib/phlexi/display/components/enum.rb
192
211
  - lib/phlexi/display/components/hint.rb
212
+ - lib/phlexi/display/components/integer.rb
213
+ - lib/phlexi/display/components/json.rb
193
214
  - lib/phlexi/display/components/label.rb
194
215
  - lib/phlexi/display/components/number.rb
216
+ - lib/phlexi/display/components/password.rb
195
217
  - lib/phlexi/display/components/placeholder.rb
196
218
  - lib/phlexi/display/components/string.rb
219
+ - lib/phlexi/display/components/time.rb
220
+ - lib/phlexi/display/components/url.rb
197
221
  - lib/phlexi/display/components/wrapper.rb
198
- - lib/phlexi/display/field_options/associations.rb
199
- - lib/phlexi/display/field_options/attachments.rb
200
- - lib/phlexi/display/field_options/description.rb
201
- - lib/phlexi/display/field_options/hints.rb
202
- - lib/phlexi/display/field_options/inferred_types.rb
203
- - lib/phlexi/display/field_options/labels.rb
204
- - lib/phlexi/display/field_options/placeholders.rb
205
- - lib/phlexi/display/field_options/themes.rb
206
- - lib/phlexi/display/option_mapper.rb
207
- - lib/phlexi/display/structure/dom.rb
208
- - lib/phlexi/display/structure/field_builder.rb
209
- - lib/phlexi/display/structure/field_collection.rb
210
- - lib/phlexi/display/structure/namespace.rb
211
- - lib/phlexi/display/structure/namespace_collection.rb
212
- - lib/phlexi/display/structure/node.rb
222
+ - lib/phlexi/display/html.rb
223
+ - lib/phlexi/display/options/inferred_types.rb
224
+ - lib/phlexi/display/theme.rb
213
225
  - lib/phlexi/display/version.rb
214
226
  - sig/phlexi/display.rbs
215
227
  homepage: https://github.com/radioactive-labs/phlexi-display
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlexi
4
- module Display
5
- module FieldOptions
6
- module Associations
7
- protected
8
-
9
- def association_reflection
10
- @association_reflection ||= find_association_reflection
11
- end
12
-
13
- def find_association_reflection
14
- if object.class.respond_to?(:reflect_on_association)
15
- object.class.reflect_on_association(key)
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,21 +0,0 @@
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
@@ -1,22 +0,0 @@
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
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlexi
4
- module Display
5
- module FieldOptions
6
- module Hints
7
- def hint(hint = nil)
8
- if hint.nil?
9
- options[:hint]
10
- else
11
- options[:hint] = hint
12
- self
13
- end
14
- end
15
-
16
- def has_hint?
17
- hint.present?
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,129 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bigdecimal"
4
-
5
- module Phlexi
6
- module Display
7
- module FieldOptions
8
- module InferredTypes
9
- def inferred_db_type
10
- @inferred_db_type ||= infer_db_type
11
- end
12
-
13
- def inferred_display_component
14
- @inferred_display_component ||= infer_display_component
15
- end
16
-
17
- private
18
-
19
- def infer_display_component
20
- case inferred_db_type
21
- when :string, :text
22
- infer_string_display_type(key)
23
- when :integer, :float, :decimal
24
- :number
25
- when :date, :datetime, :time
26
- :date
27
- when :boolean
28
- :boolean
29
- when :json, :jsonb, :hstore
30
- :code
31
- else
32
- if association_reflection
33
- :association
34
- elsif attachment_reflection
35
- :attachment
36
- else
37
- :text
38
- end
39
- end
40
- end
41
-
42
- def infer_db_type
43
- if object.class.respond_to?(:columns_hash)
44
- # ActiveRecord object
45
- column = object.class.columns_hash[key.to_s]
46
- return column.type if column
47
- end
48
-
49
- if object.class.respond_to?(:attribute_types)
50
- # ActiveModel::Attributes
51
- custom_type = object.class.attribute_types[key.to_s]
52
- return custom_type.type if custom_type
53
- end
54
-
55
- # Check if object responds to the key
56
- if object.respond_to?(key)
57
- # Fallback to inferring type from the value
58
- return infer_db_type_from_value(object.send(key))
59
- end
60
-
61
- # Default to string if we can't determine the type
62
- :string
63
- end
64
-
65
- def infer_db_type_from_value(value)
66
- case value
67
- when Integer
68
- :integer
69
- when Float
70
- :float
71
- when BigDecimal
72
- :decimal
73
- when TrueClass, FalseClass
74
- :boolean
75
- when Date
76
- :date
77
- when Time, DateTime
78
- :datetime
79
- when Hash
80
- :json
81
- else
82
- :string
83
- end
84
- end
85
-
86
- def infer_string_display_type(key)
87
- key = key.to_s.downcase
88
-
89
- return :password if is_password_field?
90
-
91
- custom_type = custom_string_display_type(key)
92
- return custom_type if custom_type
93
-
94
- :text
95
- end
96
-
97
- def custom_string_display_type(key)
98
- custom_mappings = {
99
- /url$|^link|^site/ => :url,
100
- /^email/ => :email,
101
- /phone|tel(ephone)?/ => :phone,
102
- /^time/ => :time,
103
- /^date/ => :date,
104
- /^number|_count$|_amount$/ => :number,
105
- /^color/ => :color
106
- }
107
-
108
- custom_mappings.each do |pattern, type|
109
- return type if key.match?(pattern)
110
- end
111
-
112
- nil
113
- end
114
-
115
- def is_password_field?
116
- key = self.key.to_s.downcase
117
-
118
- exact_matches = ["password"]
119
- prefixes = ["encrypted_"]
120
- suffixes = ["_password", "_digest", "_hash"]
121
-
122
- exact_matches.include?(key) ||
123
- prefixes.any? { |prefix| key.start_with?(prefix) } ||
124
- suffixes.any? { |suffix| key.end_with?(suffix) }
125
- end
126
- end
127
- end
128
- end
129
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlexi
4
- module Display
5
- module FieldOptions
6
- module Labels
7
- def label(label = nil)
8
- if label.nil?
9
- options[:label] = options.fetch(:label) { calculate_label }
10
- else
11
- options[:label] = label
12
- self
13
- end
14
- end
15
-
16
- private
17
-
18
- def calculate_label
19
- if object.class.respond_to?(:human_attribute_name)
20
- object.class.human_attribute_name(key.to_s, {base: object})
21
- else
22
- key.to_s.humanize
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlexi
4
- module Display
5
- module FieldOptions
6
- module Placeholders
7
- def placeholder(placeholder = nil)
8
- if placeholder.nil?
9
- options[:placeholder] || "-"
10
- else
11
- options[:placeholder] = placeholder
12
- self
13
- end
14
- end
15
- end
16
- end
17
- end
18
- end
@@ -1,132 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlexi
4
- module Display
5
- module FieldOptions
6
- module Themes
7
- # Resolves theme classes for components based on their type.
8
- #
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.
11
- #
12
- # @param component [Symbol, String] The type of display component (e.g., :text, :date, :boolean)
13
- #
14
- # @return [String, nil] A string of CSS classes for the component, or nil if no theme is applied
15
- #
16
- # @example Basic usage
17
- # themed(:text)
18
- # # => "text-gray-700 text-sm"
19
- #
20
- # @example Cascading themes
21
- # # Assuming email inherits from text in the theme definition
22
- # themed(:email)
23
- # # => "text-gray-700 text-sm text-blue-600 underline"
24
- #
25
- # @note The actual CSS classes returned will depend on the theme definitions in the `theme` hash
26
- # and any overrides specified in the `options` hash.
27
- #
28
- # @see #resolve_theme
29
- # @see #theme
30
- def themed(component)
31
- return unless component
32
-
33
- resolve_theme(component)
34
- end
35
-
36
- protected
37
-
38
- # Recursively resolves the theme for a given property, handling nested symbol references
39
- #
40
- # @param property [Symbol, String] The theme property to resolve
41
- # @param visited [Set] Set of already visited properties to prevent infinite recursion
42
- # @return [String, nil] The resolved theme value or nil if not found
43
- #
44
- # @example Resolving a nested theme
45
- # # Assuming the theme is: { email: :text, text: "text-gray-700" }
46
- # resolve_theme(:email)
47
- # # => "text-gray-700"
48
- def resolve_theme(property, visited = Set.new)
49
- return nil if !property.present? || visited.include?(property)
50
- visited.add(property)
51
-
52
- result = theme[property]
53
- if result.is_a?(Symbol)
54
- resolve_theme(result, visited)
55
- else
56
- result
57
- end
58
- end
59
-
60
- # Retrieves or initializes the theme hash for the display builder.
61
- #
62
- # This method returns a hash containing theme definitions for various display components.
63
- # If a theme has been explicitly set in the options, it returns that. Otherwise, it
64
- # initializes and returns a default theme.
65
- #
66
- # The theme hash defines CSS classes or references to other theme keys for different
67
- # components.
68
- #
69
- # @return [Hash] A hash containing theme definitions for display components
70
- #
71
- # @example Accessing the theme
72
- # theme[:text]
73
- # # => "text-gray-700 text-sm"
74
- #
75
- # @example Theme inheritance
76
- # theme[:email] # Returns :text, indicating email inherits text's theme
77
- #
78
- # @note The actual content of the theme hash depends on the default_theme method
79
- # and any theme overrides specified in the options when initializing the field builder.
80
- #
81
- # @see #default_theme
82
- def theme
83
- @theme ||= options[:theme] || default_theme
84
- end
85
-
86
- # Defines and returns the default theme hash for the display builder.
87
- #
88
- # This method returns a hash containing the base theme definitions for various components.
89
- # It sets up the default styling and relationships between different components.
90
- # The theme uses a combination of explicit CSS classes and symbolic references to other theme keys,
91
- # allowing for a flexible and inheritance-based theming system.
92
- #
93
- # @return [Hash] A frozen hash containing default theme definitions for components
94
- #
95
- # @example Accessing the default theme
96
- # default_theme[:text]
97
- # # => "text-gray-700 text-sm"
98
- #
99
- # @example Theme inheritance
100
- # default_theme[:email]
101
- # # => :text (indicates that :email inherits from :text)
102
- #
103
- # @note This method returns a frozen hash to prevent accidental modifications.
104
- # To customize the theme, users should provide their own theme hash when initializing the display builder.
105
- #
106
- # @see #theme
107
- def default_theme
108
- {
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
127
- }.freeze
128
- end
129
- end
130
- end
131
- end
132
- end