phlexi-table 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.ruby-version +1 -0
  4. data/Appraisals +8 -0
  5. data/CHANGELOG.md +5 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +13 -0
  8. data/Rakefile +14 -0
  9. data/TODO +0 -0
  10. data/config.ru +6 -0
  11. data/gemfiles/default.gemfile +5 -0
  12. data/gemfiles/rails_7.gemfile +8 -0
  13. data/lib/phlexi/table/base.rb +119 -0
  14. data/lib/phlexi/table/components/base.rb +38 -0
  15. data/lib/phlexi/table/components/concerns/displays_value.rb +54 -0
  16. data/lib/phlexi/table/components/date_time.rb +49 -0
  17. data/lib/phlexi/table/components/description.rb +21 -0
  18. data/lib/phlexi/table/components/hint.rb +21 -0
  19. data/lib/phlexi/table/components/label.rb +15 -0
  20. data/lib/phlexi/table/components/number.rb +37 -0
  21. data/lib/phlexi/table/components/placeholder.rb +15 -0
  22. data/lib/phlexi/table/components/string.rb +17 -0
  23. data/lib/phlexi/table/components/wrapper.rb +17 -0
  24. data/lib/phlexi/table/field_options/associations.rb +21 -0
  25. data/lib/phlexi/table/field_options/attachments.rb +21 -0
  26. data/lib/phlexi/table/field_options/description.rb +22 -0
  27. data/lib/phlexi/table/field_options/hints.rb +22 -0
  28. data/lib/phlexi/table/field_options/inferred_types.rb +129 -0
  29. data/lib/phlexi/table/field_options/labels.rb +28 -0
  30. data/lib/phlexi/table/field_options/placeholders.rb +18 -0
  31. data/lib/phlexi/table/field_options/themes.rb +132 -0
  32. data/lib/phlexi/table/structure/dom.rb +42 -0
  33. data/lib/phlexi/table/structure/field_builder.rb +158 -0
  34. data/lib/phlexi/table/structure/field_collection.rb +39 -0
  35. data/lib/phlexi/table/structure/namespace.rb +123 -0
  36. data/lib/phlexi/table/structure/namespace_collection.rb +40 -0
  37. data/lib/phlexi/table/structure/node.rb +24 -0
  38. data/lib/phlexi/table/version.rb +7 -0
  39. data/lib/phlexi/table.rb +30 -0
  40. data/lib/phlexi-table.rb +3 -0
  41. data/sig/phlexi/table.rbs +6 -0
  42. metadata +241 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e05e322aef835f61e4d9c75e678c54ed3abd96675b7eaedb7db6b8138f826450
4
+ data.tar.gz: 3d2e8b90865ab5e8e5b5b405046277a51177508187f3fde2d708612bed01dc6f
5
+ SHA512:
6
+ metadata.gz: e9e99ffd0dff1b5e92cdc779c8cd3fd5dee46e20094916e50eeec337c1af2b88fd0b9fa637dd56f54ad3a12381e0d2b2434747d536a4bb5fff928d08ccd6dd1f
7
+ data.tar.gz: 4f97e0de92ea6e09b17d7e09c6d242ee968638c6d4766991515aa54d3e738a51219190ca4d1fd944205b06b627eb9c3a2b0e7125adf5cb22eda839f14a5c7668
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.2
data/Appraisals ADDED
@@ -0,0 +1,8 @@
1
+ appraise "default" do
2
+ # gem "phlex", "~> 1.10"
3
+ end
4
+
5
+ appraise "rails-7" do
6
+ gem "rails", "~> 7.1.3", ">= 7.1.3.4"
7
+ gem "sqlite3", "~> 1.4"
8
+ end
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.2.0] - 2024-07-31
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Stefan Froelich
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # Phlexi::Table
2
+
3
+ Phlexi::Table is a flexible and powerful table builder for Ruby applications. It provides a more customizable and extensible way to create tables.
4
+
5
+ [![Ruby](https://github.com/radioactive-labs/phlexi-display/actions/workflows/main.yml/badge.svg)](https://github.com/radioactive-labs/phlexi-display/actions/workflows/main.yml)
6
+
7
+ ## Contributing
8
+
9
+ Bug reports and pull requests are welcome on GitHub at https://github.com/radioactive-labs/phlexi-display.
10
+
11
+ ## License
12
+
13
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+ require "standard/rake"
6
+
7
+ task default: %i[test standard]
8
+
9
+ # https://juincc.medium.com/how-to-setup-minitest-for-your-gems-development-f29c4bee13c2
10
+ Rake::TestTask.new do |t|
11
+ t.libs << "test"
12
+ t.test_files = FileList["test/**/*_test.rb"]
13
+ t.verbose = true
14
+ end
data/TODO ADDED
File without changes
data/config.ru ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems"
4
+ require "bundler"
5
+
6
+ Bundler.require :default, :development
@@ -0,0 +1,5 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 7.1.3", ">= 7.1.3.4"
6
+ gem "sqlite3", "~> 1.4"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/delegation"
4
+ require "active_support/core_ext/string/inflections"
5
+
6
+ module Phlexi
7
+ module Table
8
+ # A display component for rendering flexible and customizable data views.
9
+ #
10
+ # @example Basic usage
11
+ # Phlexi::Table.new(user) do |d|
12
+ # render d.field(:name).text
13
+ # render d.field(:email).text
14
+ # end
15
+ #
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
18
+ class Base < COMPONENT_BASE
19
+ class Namespace < Structure::Namespace; end
20
+
21
+ class FieldBuilder < Structure::FieldBuilder; end
22
+
23
+ attr_reader :key, :object
24
+
25
+ delegate :field, :nest_one, :nest_many, to: :@namespace
26
+
27
+ # Initializes a new Table instance.
28
+ #
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
33
+ # @option options [Class] :namespace_klass Custom namespace class
34
+ # @option options [Class] :builder_klass Custom field builder class
35
+ def initialize(record, attributes: {}, **options)
36
+ @display_class = options.delete(:class)
37
+ @attributes = attributes
38
+ @namespace_klass = options.delete(:namespace_klass) || default_namespace_klass
39
+ @builder_klass = options.delete(:builder_klass) || default_builder_klass
40
+ @options = options
41
+
42
+ initialize_object_and_key(record)
43
+ initialize_namespace
44
+ end
45
+
46
+ # Renders the display template.
47
+ #
48
+ # @return [void]
49
+ def view_template
50
+ display_template
51
+ end
52
+
53
+ # Executes the display's content block.
54
+ # Override this in subclasses to define a static display.
55
+ #
56
+ # @return [void]
57
+ def display_template
58
+ instance_exec(&@_content_block) if @_content_block
59
+ end
60
+
61
+ protected
62
+
63
+ attr_reader :options, :attributes, :namespace_klass, :builder_klass
64
+
65
+ # Initializes the object and key based on the given record.
66
+ #
67
+ # @param record [ActiveModel::Model, Symbol, String] The display's associated record or key
68
+ # @return [void]
69
+ def initialize_object_and_key(record)
70
+ @key = options.delete(:key) || options.delete(:as)
71
+
72
+ case record
73
+ when String, Symbol
74
+ @object = nil
75
+ @key = record
76
+ else
77
+ @object = record
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
82
+ end
83
+ end
84
+ @key = @key.to_sym
85
+ end
86
+
87
+ # Initializes the namespace for the display.
88
+ #
89
+ # @return [void]
90
+ def initialize_namespace
91
+ @namespace = namespace_klass.root(key, object: object, builder_klass: builder_klass)
92
+ end
93
+ # Retrieves the display's CSS classes.
94
+ #
95
+ # @return [String] The display's CSS classes
96
+ attr_reader :display_class
97
+
98
+ # Generates the display attributes hash.
99
+ #
100
+ # @return [Hash] The display attributes
101
+ def display_attributes
102
+ mix({
103
+ id: @namespace.dom_id,
104
+ class: display_class
105
+ }, attributes)
106
+ end
107
+
108
+ private
109
+
110
+ def default_namespace_klass
111
+ self.class::Namespace
112
+ end
113
+
114
+ def default_builder_klass
115
+ self.class::FieldBuilder
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Table
5
+ module Components
6
+ class Base < COMPONENT_BASE
7
+ attr_reader :field, :attributes
8
+
9
+ def initialize(field, **attributes)
10
+ @field = field
11
+ @attributes = attributes
12
+
13
+ build_attributes
14
+ append_attribute_classes
15
+ end
16
+
17
+ protected
18
+
19
+ def build_attributes
20
+ attributes.fetch(:id) { attributes[:id] = "#{field.dom.id}_#{component_name}" }
21
+ end
22
+
23
+ def append_attribute_classes
24
+ return if attributes[:class] == false
25
+
26
+ attributes[:class] = tokens(
27
+ component_name,
28
+ attributes[:class]
29
+ )
30
+ end
31
+
32
+ def component_name
33
+ @component_name ||= self.class.name.demodulize.underscore
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Table
5
+ module Components
6
+ module Concerns
7
+ module TablesValue
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 Table
5
+ module Components
6
+ class DateTime < Base
7
+ include Concerns::TablesValue
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
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Table
5
+ module Components
6
+ class Description < Base
7
+ def view_template
8
+ p(**attributes) {
9
+ field.description
10
+ }
11
+ end
12
+
13
+ private
14
+
15
+ def render?
16
+ field.has_description?
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Table
5
+ module Components
6
+ class Hint < Base
7
+ def view_template
8
+ p(**attributes) do
9
+ field.hint
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def render?
16
+ field.has_hint?
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Table
5
+ module Components
6
+ class Label < Base
7
+ def view_template
8
+ h5(**attributes) {
9
+ field.label
10
+ }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/number_helper"
4
+
5
+ module Phlexi
6
+ module Table
7
+ module Components
8
+ class Number < Base
9
+ include Concerns::TablesValue
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 Table
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 Table
5
+ module Components
6
+ class String < Base
7
+ include Concerns::TablesValue
8
+
9
+ def render_value(value)
10
+ p(**attributes) {
11
+ value
12
+ }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Table
5
+ module Components
6
+ class Wrapper < Base
7
+ def view_template
8
+ div(**attributes) {
9
+ render field.label_tag
10
+ yield field if block_given?
11
+ render field.description_tag
12
+ }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Table
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
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Table
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 Table
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
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlexi
4
+ module Table
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