phlexi-table 0.0.1
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 +7 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/Appraisals +8 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +13 -0
- data/Rakefile +14 -0
- data/TODO +0 -0
- data/config.ru +6 -0
- data/gemfiles/default.gemfile +5 -0
- data/gemfiles/rails_7.gemfile +8 -0
- data/lib/phlexi/table/base.rb +119 -0
- data/lib/phlexi/table/components/base.rb +38 -0
- data/lib/phlexi/table/components/concerns/displays_value.rb +54 -0
- data/lib/phlexi/table/components/date_time.rb +49 -0
- data/lib/phlexi/table/components/description.rb +21 -0
- data/lib/phlexi/table/components/hint.rb +21 -0
- data/lib/phlexi/table/components/label.rb +15 -0
- data/lib/phlexi/table/components/number.rb +37 -0
- data/lib/phlexi/table/components/placeholder.rb +15 -0
- data/lib/phlexi/table/components/string.rb +17 -0
- data/lib/phlexi/table/components/wrapper.rb +17 -0
- data/lib/phlexi/table/field_options/associations.rb +21 -0
- data/lib/phlexi/table/field_options/attachments.rb +21 -0
- data/lib/phlexi/table/field_options/description.rb +22 -0
- data/lib/phlexi/table/field_options/hints.rb +22 -0
- data/lib/phlexi/table/field_options/inferred_types.rb +129 -0
- data/lib/phlexi/table/field_options/labels.rb +28 -0
- data/lib/phlexi/table/field_options/placeholders.rb +18 -0
- data/lib/phlexi/table/field_options/themes.rb +132 -0
- data/lib/phlexi/table/structure/dom.rb +42 -0
- data/lib/phlexi/table/structure/field_builder.rb +158 -0
- data/lib/phlexi/table/structure/field_collection.rb +39 -0
- data/lib/phlexi/table/structure/namespace.rb +123 -0
- data/lib/phlexi/table/structure/namespace_collection.rb +40 -0
- data/lib/phlexi/table/structure/node.rb +24 -0
- data/lib/phlexi/table/version.rb +7 -0
- data/lib/phlexi/table.rb +30 -0
- data/lib/phlexi-table.rb +3 -0
- data/sig/phlexi/table.rbs +6 -0
- 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
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.2.2
|
data/Appraisals
ADDED
data/CHANGELOG.md
ADDED
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
|
+
[](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,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,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,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
|