phlexi-display 0.0.2 → 0.0.4

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 (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